From a15f4fc3cd3609e973b02cc7b974631e26c07d52 Mon Sep 17 00:00:00 2001 From: Michael ZBYSZYNSKI Date: Tue, 14 Nov 2023 10:14:49 +0000 Subject: [PATCH] Revert "style and silencing warnings" This reverts commit 612149fa5663af28a7d5d56aebf1586c7554ac5c. --- src/dtw.cpp | 232 +++++------ src/neuralNetwork.cpp | 756 +++++++++++++++++------------------ src/regression.cpp | 332 ++++++++------- src/regression.h | 86 ++-- src/seriesClassification.cpp | 490 +++++++++++------------ src/seriesClassification.h | 219 +++++----- 6 files changed, 1020 insertions(+), 1095 deletions(-) diff --git a/src/dtw.cpp b/src/dtw.cpp index 70636ca0..2b6394c6 100644 --- a/src/dtw.cpp +++ b/src/dtw.cpp @@ -22,160 +22,148 @@ dtw::~dtw() {}; template inline T dtw::distanceFunction(const std::vector &x, const std::vector &y) { - double euclidianDistance {}; - - if (x.size() != y.size()) - { - throw std::length_error("comparing different length series"); - } - else - { - for (std::size_t i {}; i < x.size(); ++i) + double euclidianDistance = 0; + if (x.size() != y.size()) { - euclidianDistance += pow((x[i] - y[i]), 2); + throw std::length_error("comparing different length series"); } - - euclidianDistance = sqrt(euclidianDistance); - } - return (T)euclidianDistance; + else + { + for (std::size_t i = 0; i < x.size(); ++i) + { + euclidianDistance = euclidianDistance + pow((x[i] - y[i]), 2); + } + + euclidianDistance = sqrt(euclidianDistance); + } + return (T)euclidianDistance; }; /* Just returns the cost, doesn't calculate the path */ template T dtw::getCost(const std::vector > &seriesX, const std::vector > &seriesY) { - if (seriesX.size() < seriesY.size()) - { - return getCost(seriesY, seriesX); - } - - costMatrix.clear(); - - for (std::size_t i {}; i < seriesX.size(); ++i) - { - std::vector tempVector(seriesY.size(), 0); - costMatrix.push_back(tempVector); - } - - const std::size_t maxX { seriesX.size() - 1 }; - const std::size_t maxY { seriesY.size() - 1 }; - - //Calculate values for the first column - costMatrix[0][0] = distanceFunction(seriesX[0], seriesY[0]); - - for (std::size_t j {}; j < maxY; ++j) - { - costMatrix[0][j + 1] = costMatrix[0][j] + distanceFunction(seriesX[0], seriesY[j + 1]); - } - - for (std::size_t i {}; i < maxX; ++i) - { - //Bottom row of current column - costMatrix[i + 1][0] = costMatrix[i][0] + distanceFunction(seriesX[i + 1], seriesY[0]); - - for (std::size_t j {}; j < maxY; ++j) + if (seriesX.size() < seriesY.size()) { - const T minGlobalCost = fmin(costMatrix[i][j], costMatrix[i + 1][j]); - costMatrix[i + 1][j] = minGlobalCost + distanceFunction(seriesX[i + 1], seriesY[j]); + return getCost(seriesY, seriesX); } - } - return costMatrix[maxX][maxY]; -}; - -template -warpPath dtw::calculatePath(const std::size_t seriesXsize, const std::size_t seriesYsize) const -{ - warpPath warpPath; - std::size_t i { seriesXsize - 1 }; - std::size_t j { seriesYsize - 1 }; - warpPath.add(i, j); - - while ((i > 0) || (j > 0)) - { - const T diagonalCost = ((i > 0) && (j > 0)) ? costMatrix[i - 1][j - 1] : std::numeric_limits::infinity(); - const T leftCost = (i > 0) ? costMatrix[i - 1][j] : std::numeric_limits::infinity(); - const T downCost = (j > 0) ? costMatrix[i][j - 1] : std::numeric_limits::infinity(); - if ((diagonalCost <= leftCost) && (diagonalCost <= downCost)) + costMatrix.clear(); + for (auto framesX : seriesX) { - if (i > 0) --i; - if (j > 0) --j; + std::vector tempVector; + for (auto framesY : seriesY) + { + tempVector.push_back(0); + } + costMatrix.push_back(tempVector); } - else if ((leftCost < diagonalCost) && (leftCost < downCost)) - { - --i; - } - else if ((downCost < diagonalCost) && (downCost < leftCost)) - { - --j; + std::size_t maxX = seriesX.size() - 1; + std::size_t maxY = seriesY.size() - 1; + + //Calculate values for the first column + costMatrix[0][0] = distanceFunction(seriesX[0], seriesY[0]); + for (int j = 1; j <= maxY; ++j) { + costMatrix[0][j] = costMatrix[0][j - 1] + distanceFunction(seriesX[0], seriesY[j]); } - else if (i <= j) + + for (std::size_t i = 1; i <= maxX; ++i) { - --j; + //Bottom row of current column + costMatrix[i][0] = costMatrix[i - 1][0] + distanceFunction(seriesX[i], seriesY[0]); + + for (std::size_t j = 1; j <= maxY; ++j) + { + T minGlobalCost = fmin(costMatrix[i-1][j-1], costMatrix[i][j-1]); + costMatrix[i][j] = minGlobalCost + distanceFunction(seriesX[i], seriesY[j]); + } } - else + return costMatrix[maxX][maxY]; +}; + +template +warpPath dtw::calculatePath(std::size_t seriesXsize, std::size_t seriesYsize) const +{ + warpPath warpPath; + std::size_t i = seriesXsize - 1; + std::size_t j = seriesYsize - 1; + warpPath.add(i, j); + + while ((i > 0) || (j > 0)) { - --i; + T diagonalCost = ((i > 0) && (j > 0)) ? costMatrix[i - 1][j - 1] : std::numeric_limits::infinity(); + T leftCost = (i > 0) ? costMatrix[i - 1][j] : std::numeric_limits::infinity(); + T downCost = (j > 0) ? costMatrix[i][j - 1] : std::numeric_limits::infinity(); + + if ((diagonalCost <= leftCost) && (diagonalCost <= downCost)) + { + if (i > 0) --i; + if (j > 0) --j; + } + else if ((leftCost < diagonalCost) && (leftCost < downCost)) + { + --i; + } + else if ((downCost < diagonalCost) && (downCost < leftCost)) + { + --j; + } + else if (i <= j) + { + --j; + } + else + { + --i; + } + warpPath.add(i, j); } - - warpPath.add(i, j); - } - - return warpPath; + return warpPath; }; /* calculates both the cost and the warp path*/ template warpInfo dtw::dynamicTimeWarp(const std::vector > &seriesX, const std::vector > &seriesY) { - warpInfo info; - - //calculate cost matrix - info.cost = getCost(seriesX, seriesY); - info.path = calculatePath(seriesX.size(), seriesY.size()); - return info; + warpInfo info; + //calculate cost matrix + info.cost = getCost(seriesX, seriesY); + info.path = calculatePath(seriesX.size(), seriesY.size()); + return info; } /* calculates warp info based on window */ template warpInfo dtw::constrainedDTW(const std::vector > &seriesX, const std::vector > &seriesY, searchWindow window) { - //initialize cost matrix - costMatrix.clear(); - std::vector tempVector(seriesY.size(), std::numeric_limits::max()); - costMatrix.assign(seriesX.size(), tempVector); //TODO: this could be smaller, since most cells are unused - const std::size_t maxX { seriesX.size() - 1 }; - const std::size_t maxY { seriesY.size() - 1 }; - - //fill cost matrix cells based on window - for (std::size_t currentX {}; currentX < window.minMaxValues.size(); ++currentX) - { - for (std::size_t currentY = window.minMaxValues[currentX].first; currentY <= window.minMaxValues[currentX].second; ++currentY) //FIXME: should be <= ? + //initialize cost matrix + costMatrix.clear(); + std::vector tempVector(seriesY.size(), std::numeric_limits::max()); + costMatrix.assign(seriesX.size(), tempVector); //TODO: this could be smaller, since most cells are unused + std::size_t maxX = seriesX.size() - 1; + std::size_t maxY = seriesY.size() - 1; + + //fill cost matrix cells based on window + for (std::size_t currentX = 0; currentX < window.minMaxValues.size(); ++currentX) { - if (currentX == 0 && currentY == 0) //bottom left cell - { - costMatrix[0][0] = distanceFunction(seriesX[0], seriesY[0]); - } - else if (currentX == 0) //first column - { - costMatrix[0][currentY] = distanceFunction(seriesX[0], seriesY[currentY]) + costMatrix[0][currentY - 1]; - } - else if (currentY == 0) //first row - { - costMatrix[currentX][0] = distanceFunction(seriesX[currentX], seriesY[0]) + costMatrix[currentX - 1][0]; - } - else - { - T minGlobalCost = fmin(costMatrix[currentX - 1][currentY], fmin(costMatrix[currentX-1][currentY-1], costMatrix[currentX][currentY-1])); - costMatrix[currentX][currentY] = distanceFunction(seriesX[currentX], seriesY[currentY]) + minGlobalCost; - } + for (std::size_t currentY = window.minMaxValues[currentX].first; currentY <= window.minMaxValues[currentX].second; ++currentY) //FIXME: should be <= ? + { + if (currentX == 0 && currentY == 0) { //bottom left cell + costMatrix[0][0] = distanceFunction(seriesX[0], seriesY[0]); + } else if (currentX == 0) { //first column + costMatrix[0][currentY] = distanceFunction(seriesX[0], seriesY[currentY]) + costMatrix[0][currentY - 1]; + } else if (currentY == 0) { //first row + costMatrix[currentX][0] = distanceFunction(seriesX[currentX], seriesY[0]) + costMatrix[currentX - 1][0]; + } else { + T minGlobalCost = fmin(costMatrix[currentX - 1][currentY], fmin(costMatrix[currentX-1][currentY-1], costMatrix[currentX][currentY-1])); + costMatrix[currentX][currentY] = distanceFunction(seriesX[currentX], seriesY[currentY]) + minGlobalCost; + } + } } - } - - warpInfo info; - info.cost = costMatrix[maxX][maxY]; - info.path = calculatePath(seriesX.size(), seriesY.size()); - return info; + warpInfo info; + info.cost = costMatrix[maxX][maxY]; + info.path = calculatePath(seriesX.size(), seriesY.size()); + return info; } //explicit instantiation diff --git a/src/neuralNetwork.cpp b/src/neuralNetwork.cpp index 13c96772..21e30b02 100644 --- a/src/neuralNetwork.cpp +++ b/src/neuralNetwork.cpp @@ -19,19 +19,17 @@ template void neuralNetwork::initTrainer() { - //initialize deltas - //FIXME: This creates a vector of numHiddenLayers x numHiddenNodes x numInputs. It fails between hidden vectors if numHiddenNodes > numInputs. - //This hacky fix makes it too big if there are more hidden nodes. Shouldn't crash, though. - if (numHiddenNodes > numInputs) - { - deltaWeights = std::vector > >(numHiddenLayers, std::vector >(numHiddenNodes, std::vector((numHiddenNodes + 1), 0))); - } - else - { - deltaWeights = std::vector > >(numHiddenLayers, std::vector >(numHiddenNodes, std::vector((numInputs + 1), 0))); - } - - deltaHiddenOutput = std::vector((numHiddenNodes + 1), 0); + //initialize deltas + //FIXME: This creates a vector of numHiddenLayers x numHiddenNodes x numInputs. It fails between hidden vectors if numHiddenNodes > numInputs. + //This hacky fix makes it too big if there are more hidden nodes. Shouldn't crash, though. + if (numHiddenNodes > numInputs) + { + deltaWeights = std::vector > >(numHiddenLayers, std::vector >(numHiddenNodes, std::vector((numHiddenNodes + 1), 0))); + } + else { + deltaWeights = std::vector > >(numHiddenLayers, std::vector >(numHiddenNodes, std::vector((numInputs + 1), 0))); + } + deltaHiddenOutput = std::vector((numHiddenNodes + 1), 0); } /*! @@ -40,75 +38,74 @@ void neuralNetwork::initTrainer() template neuralNetwork::neuralNetwork(const size_t& num_inputs, - const std::vector& which_inputs, - const size_t& num_hidden_layers, - const size_t& num_hidden_nodes, - const std::vector& _weights, - const std::vector& w_hidden_output, - const std::vector& in_ranges, - const std::vector& in_bases, - const T& out_range, - const T& out_base - ) -: -numInputs(num_inputs), -whichInputs(which_inputs), -numHiddenLayers(num_hidden_layers), -numHiddenNodes(num_hidden_nodes), -wHiddenOutput(w_hidden_output), -inRanges(in_ranges), -inBases(in_bases), -outRange(out_range), -outBase(out_base), -outputErrorGradient(0) + const std::vector& which_inputs, + const size_t& num_hidden_layers, + const size_t& num_hidden_nodes, + const std::vector& _weights, + const std::vector& w_hidden_output, + const std::vector& in_ranges, + const std::vector& in_bases, + const T& out_range, + const T& out_base +) + : + numInputs(num_inputs), + whichInputs(which_inputs), + numHiddenLayers(num_hidden_layers), + numHiddenNodes(num_hidden_nodes), + wHiddenOutput(w_hidden_output), + inRanges(in_ranges), + inBases(in_bases), + outRange(out_range), + outBase(out_base), + outputErrorGradient(0) { - bool randomize { _weights.size() ? false : true }; - std::default_random_engine generator {}; - std::uniform_real_distribution distribution(-0.5, 0.5); - - //winding up a long vector from javascript - size_t count {}; - for (std::size_t i {}; i < numHiddenLayers; ++i) - { - std::vector> layer; - - for (size_t j {}; j < numHiddenNodes; ++j) + bool randomize = _weights.size() ? false : true; + std::default_random_engine generator; + std::uniform_real_distribution distribution(-0.5, 0.5); + //winding up a long vector from javascript + size_t count = 0; + for (int i = 0; i < numHiddenLayers; ++i) { - std::vector node {}; - size_t numConnections { (i == 0) ? numInputs : numHiddenNodes }; - - for (size_t k {}; k <= numConnections; ++k) - { - if (randomize) + std::vector> layer; + for (size_t j = 0; j < numHiddenNodes; ++j) { - node.push_back(distribution(generator)); + std::vector node; + size_t numConnections = (i == 0) ? numInputs : numHiddenNodes; + for (size_t k = 0; k <= numConnections; ++k) + { + if (randomize) + { + node.push_back(distribution(generator)); + } + else { + node.push_back(_weights[count]); + } + count++; + } + layer.push_back(node); } - else + weights.push_back(layer); + } + + if (randomize) + { + for (size_t i = 0; i <= numHiddenNodes; ++i) { - node.push_back(_weights[count]); + wHiddenOutput.push_back( distribution(generator) ); } - - count++; - } - - layer.push_back(node); } - - weights.push_back(layer); - } - - if (randomize) - { - for (size_t i {}; i <= numHiddenNodes; ++i) wHiddenOutput.push_back( distribution(generator) ); - } - - for (auto inRange : inRanges) - { - if (inRange == 0.) inRange = 1.0; //Prevent divide by zero later. - } - - //trainer -- do we really need this? - initTrainer(); + + for(auto inRange : inRanges) + { + if (inRange == 0.) + { + inRange = 1.0; //Prevent divide by zero later. + } + } + + //trainer -- do we really need this? + initTrainer(); } /*! @@ -117,22 +114,22 @@ outputErrorGradient(0) template neuralNetwork::neuralNetwork(const size_t& num_inputs, - const std::vector& which_inputs, - const size_t& num_hidden_layers, - const size_t& num_hidden_nodes - ) -: -numInputs(num_inputs), -whichInputs(which_inputs), -numHiddenLayers(num_hidden_layers), -numHiddenNodes(num_hidden_nodes), -outputErrorGradient(0) + const std::vector& which_inputs, + const size_t& num_hidden_layers, + const size_t& num_hidden_nodes +) + : + numInputs(num_inputs), + whichInputs(which_inputs), + numHiddenLayers(num_hidden_layers), + numHiddenNodes(num_hidden_nodes), + outputErrorGradient(0) { - //randomize weights - reset(); - - //trainer - initTrainer(); + //randomize weights + reset(); + + //trainer + initTrainer(); } /*! @@ -147,428 +144,391 @@ neuralNetwork::~neuralNetwork() template void neuralNetwork::reset() { - std::default_random_engine generator; - std::uniform_real_distribution distribution(-0.5, 0.5); - - weights.clear(); - for (std::size_t i {}; i < numHiddenLayers; ++i) - { - std::vector> layer; - - for (size_t j {}; j < numHiddenNodes; ++j) + std::default_random_engine generator; + std::uniform_real_distribution distribution(-0.5, 0.5); + + weights.clear(); + for (int i = 0; i < numHiddenLayers; ++i) + { + std::vector> layer; + for (size_t j = 0; j < numHiddenNodes; ++j) + { + std::vector node; + size_t numConnections = (i == 0) ? numInputs : numHiddenNodes; + for (size_t k = 0; k <= numConnections; ++k) + { + node.push_back(distribution(generator)); + } + layer.push_back(node); + } + weights.push_back(layer); + } + + wHiddenOutput.clear(); + for (size_t i = 0; i <= numHiddenNodes; ++i) { - std::vector node; - size_t numConnections { (i == 0) ? numInputs : numHiddenNodes }; - - for (size_t k {}; k <= numConnections; ++k) node.push_back(distribution(generator)); - - layer.push_back(node); + wHiddenOutput.push_back(distribution(generator)); } - - weights.push_back(layer); - } - - wHiddenOutput.clear(); - for (size_t i {}; i <= numHiddenNodes; ++i) wHiddenOutput.push_back(distribution(generator)); } template inline T neuralNetwork::getHiddenErrorGradient(size_t layer, size_t neuron) { - T weightedSum {}; - - if (numHiddenLayers == 1 || layer == 0) - { - T wGradient = wHiddenOutput[neuron] * outputErrorGradient; - return hiddenNeurons[layer][neuron] * (1 - hiddenNeurons[layer][neuron]) * wGradient; - } - - if (layer == numHiddenLayers - 1) - { - for (size_t i {}; i < numHiddenNodes; ++i) + T weightedSum = 0; + if (numHiddenLayers == 1 || layer == 0) { - weightedSum += wHiddenOutput[i] * outputErrorGradient; + T wGradient = wHiddenOutput[neuron] * outputErrorGradient; + return hiddenNeurons[layer][neuron] * (1 - hiddenNeurons[layer][neuron]) * wGradient; } - } - else - { - for (size_t i {}; i < numHiddenNodes; ++i) - { - weightedSum += deltaWeights[layer + 1][neuron][i] * outputErrorGradient; + if (layer == numHiddenLayers - 1) { + for (size_t i = 0; i < numHiddenNodes; ++i) + { + weightedSum += wHiddenOutput[i] * outputErrorGradient; + } } - } - - return hiddenNeurons[layer][neuron] * (1 - hiddenNeurons[layer][neuron]) * weightedSum; + else { + for (size_t i = 0; i < numHiddenNodes; ++i) + { + weightedSum += deltaWeights[layer + 1][neuron][i] * outputErrorGradient; + } + } + return hiddenNeurons[layer][neuron] * (1 - hiddenNeurons[layer][neuron]) * weightedSum; } template inline T neuralNetwork::activationFunction(T x) { - //sigmoid - if (x < -45) //from weka, to combat overflow - { - x = 0; - } - else if (x > 45) { - x = 1; - } - else - { - x = 1 / (1 + exp(-x)); - } - - return x; + //sigmoid + if (x < -45) { //from weka, to combat overflow + x = 0; + } + else if (x > 45) { + x = 1; + } + else { + x = 1 / (1 + exp(-x)); + } + return x; } template -size_t neuralNetwork::getNumInputs() const +size_t neuralNetwork::getNumInputs() const { - return numInputs; + return numInputs; } template -std::vector neuralNetwork::getWhichInputs() const -{ - return whichInputs; +std::vector neuralNetwork::getWhichInputs() const { + return whichInputs; } - template -size_t neuralNetwork::getNumHiddenLayers() const +size_t neuralNetwork::getNumHiddenLayers() const { - return numHiddenLayers; + return numHiddenLayers; } template -void neuralNetwork::setNumHiddenLayers(size_t num_hidden_layers) -{ - numHiddenLayers = num_hidden_layers; - reset(); - initTrainer(); +void neuralNetwork::setNumHiddenLayers(size_t num_hidden_layers) { + numHiddenLayers = num_hidden_layers; + reset(); + initTrainer(); } template -size_t neuralNetwork::getNumHiddenNodes() const +size_t neuralNetwork::getNumHiddenNodes() const { - return numHiddenNodes; + return numHiddenNodes; } template -void neuralNetwork::setNumHiddenNodes(size_t num_hidden_nodes) -{ - numHiddenNodes = num_hidden_nodes; - reset(); - initTrainer(); +void neuralNetwork::setNumHiddenNodes(size_t num_hidden_nodes) { + numHiddenNodes = num_hidden_nodes; + reset(); + initTrainer(); } template -size_t neuralNetwork::getEpochs() const +size_t neuralNetwork::getEpochs() const { - return numEpochs; + return numEpochs; } template -void neuralNetwork::setEpochs(const size_t& epochs) +void neuralNetwork::setEpochs(const size_t& epochs) { - numEpochs = epochs; + numEpochs = epochs; } template -std::vector neuralNetwork::getWeights() const +std::vector neuralNetwork::getWeights() const { - std::vector flatWeights {}; - - for (auto weightsA : weights) - { - for (auto weightsB : weightsA) + std::vector flatWeights; + for (auto weightsA : weights) { - for (auto weightC : weightsB) - { - flatWeights.push_back(weightC); - } + for (auto weightsB : weightsA) + { + for (auto weightC : weightsB) + { + flatWeights.push_back(weightC); + } + } } - } - - return flatWeights; + return flatWeights; } template -std::vector neuralNetwork::getWHiddenOutput() const +std::vector neuralNetwork::getWHiddenOutput() const { - return wHiddenOutput; + return wHiddenOutput; } template -std::vector neuralNetwork::getInRanges() const +std::vector neuralNetwork::getInRanges() const { - return inRanges; + return inRanges; } template -std::vector neuralNetwork::getInBases() const +std::vector neuralNetwork::getInBases() const { - return inBases; + return inBases; } template -T neuralNetwork::getOutRange() const +T neuralNetwork::getOutRange() const { - return outRange; + return outRange; } template -T neuralNetwork::getOutBase() const +T neuralNetwork::getOutBase() const { - return outBase; + return outBase; } #ifndef EMSCRIPTEN template -void neuralNetwork::getJSONDescription(Json::Value& jsonModelDescription) +void neuralNetwork::getJSONDescription(Json::Value& jsonModelDescription) { - jsonModelDescription["modelType"] = "Neural Network"; - jsonModelDescription["numInputs"] = (int)numInputs; //FIXME: Update json::cpp? - jsonModelDescription["whichInputs"] = this->vector2json(whichInputs); - jsonModelDescription["numHiddenLayers"] = (int)numHiddenLayers; - jsonModelDescription["numHiddenNodes"] = (int)numHiddenNodes; //FIXME: Update json::cpp? - jsonModelDescription["numHiddenOutputs"] = 1; - jsonModelDescription["inRanges"] = this->vector2json(inRanges); - jsonModelDescription["inBases"] = this->vector2json(inBases); - jsonModelDescription["outRange"] = outRange; - jsonModelDescription["outBase"] = outBase; - - //Create Nodes - Json::Value nodes; - - //Output Node - Json::Value outNode; - outNode["name"] = "Linear Node 0"; - for (size_t i {}; i < numHiddenNodes; ++i) - { - std::string nodeName = "Node " + std::to_string(i + 1); - outNode[nodeName] = wHiddenOutput[i]; - } - outNode["Threshold"] = wHiddenOutput[numHiddenNodes]; - nodes.append(outNode); - - //Input nodes - for (size_t i {}; i < weights.size(); ++i) - { - //layers - for (size_t j {}; j < weights[i].size(); ++j) + jsonModelDescription["modelType"] = "Neural Network"; + jsonModelDescription["numInputs"] = (int)numInputs; //FIXME: Update json::cpp? + jsonModelDescription["whichInputs"] = this->vector2json(whichInputs); + jsonModelDescription["numHiddenLayers"] = (int)numHiddenLayers; + jsonModelDescription["numHiddenNodes"] = (int)numHiddenNodes; //FIXME: Update json::cpp? + jsonModelDescription["numHiddenOutputs"] = 1; + jsonModelDescription["inRanges"] = this->vector2json(inRanges); + jsonModelDescription["inBases"] = this->vector2json(inBases); + jsonModelDescription["outRange"] = outRange; + jsonModelDescription["outBase"] = outBase; + + //Create Nodes + Json::Value nodes; + + //Output Node + Json::Value outNode; + outNode["name"] = "Linear Node 0"; + for (size_t i = 0; i < numHiddenNodes; ++i) { - //hidden nodes - Json::Value tempNode; - tempNode["name"] = "Sigmoid Node " + std::to_string((i * numHiddenNodes) + j + 1); - - for (size_t k {}; k < weights[i][j].size() - 1; ++k) - { - //inputs + threshold aka bias - std::string connectNode { "Attrib inputs-" + std::to_string(k + 1) }; - tempNode[connectNode] = weights[i][j][k]; - } - - tempNode["Threshold"] = weights[i][j][weights[i][j].size() - 1]; - nodes.append(tempNode); + std::string nodeName = "Node " + std::to_string(i + 1); + outNode[nodeName] = wHiddenOutput[i]; } - } - - jsonModelDescription["nodes"] = nodes; + outNode["Threshold"] = wHiddenOutput[numHiddenNodes]; + nodes.append(outNode); + + //Input nodes + for (size_t i = 0; i < weights.size(); ++i) + { //layers + for (size_t j = 0; j < weights[i].size(); ++j) + { //hidden nodes + Json::Value tempNode; + tempNode["name"] = "Sigmoid Node " + std::to_string((i * numHiddenNodes) + j + 1); + for (size_t k = 0; k < weights[i][j].size() - 1; ++k) + { //inputs + threshold aka bias + std::string connectNode = "Attrib inputs-" + std::to_string(k + 1); + tempNode[connectNode] = weights[i][j][k]; + } + tempNode["Threshold"] = weights[i][j][weights[i][j].size() - 1]; + nodes.append(tempNode); + } + } + + jsonModelDescription["nodes"] = nodes; } #endif template T neuralNetwork::run(const std::vector& inputVector) { - std::vector pattern {}; - - for (size_t h {}; h < numInputs; h++) pattern.push_back(inputVector[whichInputs[h]]); - - //set input layer - inputNeurons.clear(); - - for (size_t i {}; i < numInputs; ++i) - { - inputNeurons.push_back((pattern[i] - (inBases[i])) / inRanges[i]); - } - - inputNeurons.push_back(1); - - //calculate hidden layers - hiddenNeurons.clear(); - - for (int i {}; i < numHiddenLayers; ++i) - { - std::vector layer {}; - - for (size_t j {}; j < numHiddenNodes; ++j) - { - layer.push_back(0); - std::vector* neuronLayer {}; - - if (i == 0) //first hidden layer - { - neuronLayer = &inputNeurons; - } - else - { - neuronLayer = &hiddenNeurons[i - 1]; - } - - for (size_t k {}; k <= numInputs; ++k) layer[j] += neuronLayer->at(k) * weights[i][j][k]; - - layer[j] = activationFunction(layer[j]); + std::vector pattern; + for (size_t h = 0; h < numInputs; h++) { + pattern.push_back(inputVector[whichInputs[h]]); + } + //set input layer + inputNeurons.clear(); + for (size_t i = 0; i < numInputs; ++i) { + inputNeurons.push_back((pattern[i] - (inBases[i])) / inRanges[i]); + } + inputNeurons.push_back(1); + + //calculate hidden layers + hiddenNeurons.clear(); + for (int i = 0; i < numHiddenLayers; ++i) { + std::vector layer; + for (size_t j = 0; j < numHiddenNodes; ++j) { + layer.push_back(0); + if (i == 0) { //first hidden layer + for (size_t k = 0; k <= numInputs; ++k) { + layer[j] += inputNeurons[k] * weights[0][j][k]; + } + } + else { + for (size_t k = 0; k <= numHiddenNodes; ++k) { + layer[j] += hiddenNeurons[i - 1][k] * weights[i][j][k]; + } + } + layer[j] = activationFunction(layer[j]); + } + layer.push_back(1); //for bias weight + hiddenNeurons.push_back(layer); + } + + //calculate output + outputNeuron = 0; + for (size_t k = 0; k <= numHiddenNodes; ++k) { + outputNeuron += hiddenNeurons[numHiddenLayers - 1][k] * wHiddenOutput[k]; } - - layer.push_back(1); //for bias weight - hiddenNeurons.push_back(layer); - } - - //calculate output - outputNeuron = 0; - - for (size_t k {}; k <= numHiddenNodes; ++k) - { - outputNeuron += hiddenNeurons[numHiddenLayers - 1][k] * wHiddenOutput[k]; - } - - //if classifier, outputNeuron = activationFunction(outputNeuron), else... - outputNeuron = (outputNeuron * outRange) + outBase; - - return outputNeuron; + //if classifier, outputNeuron = activationFunction(outputNeuron), else... + outputNeuron = (outputNeuron * outRange) + outBase; + return outputNeuron; } template -void neuralNetwork::train(const std::vector >& trainingSet) -{ - train(trainingSet, 0); +void neuralNetwork::train(const std::vector >& trainingSet) { + train(trainingSet, 0); } template -void neuralNetwork::train(const std::vector >& trainingSet, const std::size_t whichOutput) +void neuralNetwork::train(const std::vector >& trainingSet, const std::size_t whichOutput) { - initTrainer(); - - //setup maxes and mins - std::vector inMax { trainingSet[0].input }; - std::vector inMin { trainingSet[0].input }; - T outMin { trainingSet[0].output[whichOutput] }; - T outMax { trainingSet[0].output[whichOutput] }; - - for (auto trainingExample : trainingSet) - { - for (size_t i {}; i < numInputs; ++i) + initTrainer(); + //setup maxes and mins + std::vector inMax = trainingSet[0].input; + std::vector inMin = trainingSet[0].input; + T outMin = trainingSet[0].output[whichOutput]; + T outMax = trainingSet[0].output[whichOutput]; + + for(auto trainingExample : trainingSet) + { + for (size_t i = 0; i < numInputs; ++i) + { + if (trainingExample.input[i] > inMax[i]) inMax[i] = trainingExample.input[i]; + if (trainingExample.input[i] < inMin[i]) inMin[i] = trainingExample.input[i]; + if (trainingExample.output[whichOutput] > outMax) outMax = trainingExample.output[whichOutput]; + if (trainingExample.output[whichOutput] < outMin) outMin = trainingExample.output[whichOutput]; + } + } + inRanges.clear(); + inBases.clear(); + + for (size_t i = 0; i < numInputs; ++i) { - if (trainingExample.input[i] > inMax[i]) inMax[i] = trainingExample.input[i]; - if (trainingExample.input[i] < inMin[i]) inMin[i] = trainingExample.input[i]; - if (trainingExample.output[whichOutput] > outMax) outMax = trainingExample.output[whichOutput]; - if (trainingExample.output[whichOutput] < outMin) outMin = trainingExample.output[whichOutput]; + inRanges.push_back((inMax[i] - inMin[i]) * 0.5); + inBases.push_back((inMax[i] + inMin[i]) * 0.5); } - } - - inRanges.clear(); - inBases.clear(); - - for (size_t i {}; i < numInputs; ++i) - { - inRanges.push_back((inMax[i] - inMin[i]) * 0.5); - inBases.push_back((inMax[i] + inMin[i]) * 0.5); - } - - for (auto inRange : inRanges) - { - if (inRange == 0.) inRange = 1.0; //Prevent divide by zero later. - } - - outRange = (outMax - outMin) * 0.5; - outBase = (outMax + outMin) * 0.5; - - //train - if (outRange) //Don't need to do any training if output never changes - { - for (currentEpoch = 0; currentEpoch < numEpochs; ++currentEpoch) + + for(auto inRange : inRanges) { - //run through every training instance - for (auto trainingExample : trainingSet) - { - run(trainingExample.input); - backpropagate(trainingExample.output[whichOutput]); - } + if (inRange == 0.) inRange = 1.0; //Prevent divide by zero later. + } + outRange = (outMax - outMin) * (T)0.5; + outBase = (outMax + outMin) * (T)0.5; + + //train + if (outRange) //Don't need to do any training if output never changes + { + for (currentEpoch = 0; currentEpoch < numEpochs; ++currentEpoch) + { + //run through every training instance + for (auto trainingExample : trainingSet) + { + run(trainingExample.input); + backpropagate(trainingExample.output[whichOutput]); + } + } } - } } template -void neuralNetwork::backpropagate(const T& desiredOutput) +void neuralNetwork::backpropagate(const T& desiredOutput) { - outputErrorGradient = ((desiredOutput - outBase) / outRange) - ((outputNeuron - outBase) / outRange); //FIXME: could be tighter -MZ - - //correction based on size of last layer. Is this right? -MZ - T length {}; - for (size_t i {}; i < numHiddenNodes; ++i) - { - length += hiddenNeurons[numHiddenLayers - 1][i] * hiddenNeurons[numHiddenLayers - 1][i]; - } - - if (length <= 2.0) length = 1.0; - - //deltas between hidden and output - for (size_t i {}; i <= numHiddenNodes; ++i) - { - deltaHiddenOutput[i] = (learningRate * (hiddenNeurons[numHiddenLayers - 1][i] / length) * outputErrorGradient) + (momentum * deltaHiddenOutput[i]); - } - - //deltas between hidden - for (std::size_t i { numHiddenLayers - 1 }; i >= 0; --i) - { - for (size_t j {}; j < numHiddenNodes; ++j) + outputErrorGradient = ((desiredOutput - outBase) / outRange) - ((outputNeuron - outBase) / outRange); //FIXME: could be tighter -MZ + + //correction based on size of last layer. Is this right? -MZ + T length = 0; + for (size_t i = 0; i < numHiddenNodes; ++i) { - T hiddenErrorGradient = getHiddenErrorGradient(i, j); - if (i > 0) - { - for (size_t k {}; k <= numHiddenNodes; ++k) - { - deltaWeights[i][j][k] = (learningRate * hiddenNeurons[i][j] * hiddenErrorGradient) + (momentum * deltaWeights[i][j][k]); - } - } - else //hidden to input layer - { - for (size_t k {}; k <= numInputs; ++k) + length += hiddenNeurons[numHiddenLayers - 1][i] * hiddenNeurons[numHiddenLayers - 1][i]; + } + if (length <= 2.0) length = 1.0; + + //deltas between hidden and output + for (size_t i = 0; i <= numHiddenNodes; ++i) + { + deltaHiddenOutput[i] = (learningRate * (hiddenNeurons[numHiddenLayers - 1][i] / length) * outputErrorGradient) + (momentum * deltaHiddenOutput[i]); + } + + //deltas between hidden + for (int i = numHiddenLayers - 1; i >= 0; --i) + { + for (size_t j = 0; j < numHiddenNodes; ++j) { - deltaWeights[0][j][k] = (learningRate * inputNeurons[k] * hiddenErrorGradient) + (momentum * deltaWeights[0][j][k]); + T hiddenErrorGradient = getHiddenErrorGradient(i, j); + if (i > 0) + { + for (size_t k = 0; k <= numHiddenNodes; ++k) + { + deltaWeights[i][j][k] = (learningRate * hiddenNeurons[i][j] * hiddenErrorGradient) + (momentum * deltaWeights[i][j][k]); + } + } + else //hidden to input layer + { + for (size_t k = 0; k <= numInputs; ++k) + { + deltaWeights[0][j][k] = (learningRate * inputNeurons[k] * hiddenErrorGradient) + (momentum * deltaWeights[0][j][k]); + } + + } } - } } - } - updateWeights(); + updateWeights(); } template void neuralNetwork::updateWeights() { - //hidden to hidden weights - for (size_t i {}; i < numHiddenLayers; ++i) - { - size_t numDeltas { (i == 0) ? numInputs : numHiddenNodes }; - for (size_t j {}; j < numHiddenNodes; ++j) + //hidden to hidden weights + for (int i = 0; i < numHiddenLayers; ++i) + { + size_t numDeltas = (i == 0) ? numInputs : numHiddenNodes; + for (size_t j = 0; j < numHiddenNodes; ++j) + { + for (size_t k = 0; k <= numDeltas; ++k) + { + weights[i][j][k] += deltaWeights[i][j][k]; + } + } + } + //hidden to output weights + for (size_t i = 0; i <= numHiddenNodes; ++i) { - for (size_t k {}; k <= numDeltas; ++k) - { - weights[i][j][k] += deltaWeights[i][j][k]; - } + wHiddenOutput[i] += deltaHiddenOutput[i]; } - } - - //hidden to output weights - for (size_t i {}; i <= numHiddenNodes; ++i) - { - wHiddenOutput[i] += deltaHiddenOutput[i]; - } } template size_t neuralNetwork::getCurrentEpoch() const { - return currentEpoch; + return currentEpoch; } //explicit instantiation diff --git a/src/regression.cpp b/src/regression.cpp index 5d33d911..60e01a64 100644 --- a/src/regression.cpp +++ b/src/regression.cpp @@ -18,234 +18,232 @@ #endif template -regressionTemplate::regressionTemplate() : -numHiddenLayers(1), -numEpochs(500), -numHiddenNodes(0) //this will be changed by training +regressionTemplate::regressionTemplate() { - modelSet::numInputs = -1; - modelSet::numOutputs = -1; - modelSet::isTraining = false; + modelSet::numInputs = -1; + modelSet::numOutputs = -1; + numHiddenLayers = 1; + numHiddenNodes = 0; //this will be changed by training + numEpochs = 500; + modelSet::isTraining = false; }; template -regressionTemplate::regressionTemplate(const int &num_inputs, const int &num_outputs) : -numHiddenLayers(1), -numEpochs(500), -numHiddenNodes(num_inputs) +regressionTemplate::regressionTemplate(const int &num_inputs, const int &num_outputs) { - modelSet::numInputs = num_inputs; - modelSet::numOutputs = num_outputs; - modelSet::isTraining = false; - created = false; - std::vector whichInputs; - - for (int i {}; i < modelSet::numInputs; ++i) - { - whichInputs.push_back(i); - } - - for (int i {}; i < modelSet::numOutputs; ++i) - { - modelSet::myModelSet.push_back(new neuralNetwork(modelSet::numInputs, whichInputs, numHiddenLayers, numHiddenNodes)); - } - - created = true; + modelSet::numInputs = num_inputs; + modelSet::numOutputs = num_outputs; + numHiddenLayers = 1; + numEpochs = 500; + numHiddenNodes = num_inputs; + modelSet::isTraining = false; + created = false; + std::vector whichInputs; + + for (int i = 0; i < modelSet::numInputs; ++i) + { + whichInputs.push_back(i); + } + + for (int i = 0; i < modelSet::numOutputs; ++i) + { + modelSet::myModelSet.push_back(new neuralNetwork(modelSet::numInputs, whichInputs, numHiddenLayers, numHiddenNodes)); + } + created = true; }; template -regressionTemplate::regressionTemplate(const std::vector > &training_set) +regressionTemplate::regressionTemplate(const std::vector > &training_set) { - modelSet::numInputs = -1; - modelSet::numOutputs = -1; - modelSet::isTraining = false; - train(training_set); + modelSet::numInputs = -1; + modelSet::numOutputs = -1; + modelSet::isTraining = false; + train(training_set); }; template -std::vector regressionTemplate::getNumHiddenLayers() const +std::vector regressionTemplate::getNumHiddenLayers() const { - std::vector vecNumHiddenLayers; - - if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) - { - for (const auto* model : modelSet::myModelSet) + std::vector vecNumHiddenLayers; + if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) + { + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + vecNumHiddenLayers.push_back(nnModel->getNumHiddenLayers()); + } + } + else { - vecNumHiddenLayers.push_back(dynamic_cast*>(model)->getNumHiddenLayers()); //FIXME: I really dislike this design + vecNumHiddenLayers = { numHiddenLayers }; } - } - else - { - vecNumHiddenLayers = { numHiddenLayers }; - } - - return vecNumHiddenLayers; + return vecNumHiddenLayers; } template void regressionTemplate::setNumHiddenLayers(const int &num_hidden_layers) { - numHiddenLayers = num_hidden_layers; - - //Set any existing models - if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) - { - for (baseModel* model : modelSet::myModelSet) + numHiddenLayers = num_hidden_layers; + //Set any existing models + if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) { - dynamic_cast*>(model)->setNumHiddenLayers(num_hidden_layers); //FIXME: I really dislike this design + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + nnModel->setNumHiddenLayers(num_hidden_layers); + } } - } } template -std::vector regressionTemplate::getNumHiddenNodes() const +std::vector regressionTemplate::getNumHiddenNodes() const { - std::vector vecNumHiddenNodes {}; - - if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) - { - for (const auto* model : modelSet::myModelSet) + std::vector vecNumHiddenNodes; + if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) + { + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + vecNumHiddenNodes.push_back(nnModel->getNumHiddenNodes()); + } + } + else { - vecNumHiddenNodes.push_back(dynamic_cast*>(model)->getNumHiddenNodes()); //FIXME: I really dislike this design + vecNumHiddenNodes = { numHiddenNodes }; } - } - else - { - vecNumHiddenNodes = { numHiddenNodes }; - } - - return vecNumHiddenNodes; + return vecNumHiddenNodes; } template void regressionTemplate::setNumHiddenNodes(const int &num_hidden_nodes) { - numHiddenNodes = num_hidden_nodes; - //Set any existing models - if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) - { - for (auto* model : modelSet::myModelSet) + numHiddenNodes = num_hidden_nodes; + //Set any existing models + if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) { - dynamic_cast*>(model)->setNumHiddenNodes(num_hidden_nodes); //FIXME: I really dislike this design + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + nnModel->setNumHiddenNodes(num_hidden_nodes); + } } - } } template -std::vector regressionTemplate::getNumEpochs() const +std::vector regressionTemplate::getNumEpochs() const { - std::vector vecEpochs; - if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) - { - for (const auto* model : modelSet::myModelSet) + std::vector vecEpochs; + if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) { - vecEpochs.push_back(dynamic_cast*>(model)->getEpochs()); //FIXME: I really dislike this design + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + vecEpochs.push_back(nnModel->getEpochs()); + } + } + else + { + vecEpochs = { numEpochs }; } - } - else - { - vecEpochs = { numEpochs }; - } - return vecEpochs; + return vecEpochs; } template -void regressionTemplate::setNumEpochs(const size_t &epochs) +void regressionTemplate::setNumEpochs(const size_t &epochs) { - numEpochs = epochs; - //set any existing models - if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) - { - for (auto* model : modelSet::myModelSet) + numEpochs = epochs; + //set any existing models + if (std::begin(modelSet::myModelSet) != std::end(modelSet::myModelSet)) { - dynamic_cast*>(model)->setEpochs(epochs); //FIXME: I really dislike this design + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + nnModel->setEpochs(epochs); + } } - } } template -bool regressionTemplate::train(const std::vector > &training_set) +bool regressionTemplate::train(const std::vector > &training_set) { - //clock_t timer; - //timer = clock(); - - modelSet::reset(); //FIXME: Should use modelSet if already created? - - if (training_set.size() > 0) - { - //create model(s) here - modelSet::numInputs = static_cast(training_set[0].input.size()); - modelSet::numOutputs = static_cast(training_set[0].output.size()); - - for (int i {}; i < modelSet::numInputs; ++i) - { - modelSet::inputNames.push_back("inputs-" + std::to_string(i + 1)); - } - - for (const auto example : training_set) - { - if (example.input.size() != modelSet::numInputs) - { - throw std::length_error("unequal feature vectors in input."); - return false; - } - - if (example.output.size() != modelSet::numOutputs) - { - throw std::length_error("unequal output vectors."); - return false; - } - } - - if (numHiddenNodes == 0) numHiddenNodes = modelSet::numInputs; - std::vector whichInputs; - - for (int j {}; j < modelSet::numInputs; ++j) - { - whichInputs.push_back(j); - } - - for (int i {}; i < modelSet::numOutputs; ++i) + //clock_t timer; + //timer = clock(); + modelSet::reset(); //FIXME: Should use modelSet if already created? + if (training_set.size() > 0) { - modelSet::myModelSet.push_back(new neuralNetwork(modelSet::numInputs, whichInputs, numHiddenLayers, numHiddenNodes)); + //create model(s) here + modelSet::numInputs = int(training_set[0].input.size()); + modelSet::numOutputs = int(training_set[0].output.size()); + + for (int i = 0; i < modelSet::numInputs; ++i) + { + modelSet::inputNames.push_back("inputs-" + std::to_string(i + 1)); + } + modelSet::numOutputs = int(training_set[0].output.size()); + + for ( auto example : training_set) + { + if (example.input.size() != modelSet::numInputs) + { + throw std::length_error("unequal feature vectors in input."); + return false; + } + if (example.output.size() != modelSet::numOutputs) + { + throw std::length_error("unequal output vectors."); + return false; + } + } + + if(numHiddenNodes == 0) numHiddenNodes = modelSet::numInputs; + std::vector whichInputs; + + for (int j = 0; j < modelSet::numInputs; ++j) + { + whichInputs.push_back(j); + } + + for (int i = 0; i < modelSet::numOutputs; ++i) + { + modelSet::myModelSet.push_back(new neuralNetwork(modelSet::numInputs, whichInputs, numHiddenLayers, numHiddenNodes)); + } + + if (numEpochs != 500) + { + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + nnModel->setEpochs(numEpochs); + } + } + + //timer = clock() - timer; + bool result = modelSet::train(training_set); + //std::cout << "Regression trained in " << (float)timer/CLOCKS_PER_SEC << " ms." << std::endl; + return result; } - - if (numEpochs != 500) - { - for (baseModel* model : modelSet::myModelSet) - { - dynamic_cast*>(model)->setEpochs(numEpochs); //FIXME: I really dislike this design - } - } - - //timer = clock() - timer; - bool result { modelSet::train(training_set) }; - //std::cout << "Regression trained in " << (float)timer/CLOCKS_PER_SEC << " ms." << std::endl; - return result; - } - - throw std::length_error("empty training set."); - return false; + throw std::length_error("empty training set."); + return false; } template float regressionTemplate::getTrainingProgress() { - float progress { modelSet::isTrained ? 1.f : 0.f }; - - if (modelSet::isTraining) - { - for (const auto* model : modelSet::myModelSet) + float progress = modelSet::isTrained ? 1.f : 0.f; + + if (modelSet::isTraining) { - const neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design - progress += (nnModel->getCurrentEpoch() / nnModel->getEpochs()); + for (baseModel* model : modelSet::myModelSet) + { + neuralNetwork* nnModel = dynamic_cast*>(model); //FIXME: I really dislike this design + progress += (nnModel->getCurrentEpoch() / nnModel->getEpochs()); + } + + progress /= modelSet::myModelSet.size(); } - - progress /= modelSet::myModelSet.size(); - } - - return progress; + + return progress; } //explicit instantiation diff --git a/src/regression.h b/src/regression.h index 776fb903..694e8288 100644 --- a/src/regression.h +++ b/src/regression.h @@ -23,56 +23,52 @@ template class regressionTemplate final : public modelSet { public: - - /** with no arguments, just make an empty vector */ - regressionTemplate(); - - /** create based on training set inputs and outputs */ - regressionTemplate(const std::vector > &trainingSet); - - /** create with proper models, but not trained */ - regressionTemplate(const int &numInputs, const int &numOutputs); - - /** destructor */ - ~regressionTemplate() {}; - - /** Train on a specified set, causes creation if not created */ - bool train(const std::vector > &trainingSet) override; - - /** Check how far the training has gotten. Averages progress over all models in training */ - float getTrainingProgress(); - - /** Check how many training epochs each model will run. This feature is temporary, and will be replaced by a different design. */ - std::vector getNumEpochs() const; - - /** Call before train, to set the number of training epochs */ - void setNumEpochs(const size_t &epochs); - - /** Check how many hidden layers are in each model. This feature is temporary, and will be replaced by a different design. */ - std::vector getNumHiddenLayers() const; - - /** Set how many hidden layers are in all models. This feature is temporary, and will be replaced by a different design. */ - void setNumHiddenLayers(const int &num_hidden_layers); - - /** Check how many hidden nodes are in each model. This feature is temporary, and will be replaced by a different design. */ - std::vector getNumHiddenNodes() const; - - /** Set how many hidden layers are in all models. This feature is temporary, and will be replaced by a different design. */ - void setNumHiddenNodes(const int &num_hidden_nodes); - + /** with no arguments, just make an empty vector */ + regressionTemplate(); + /** create based on training set inputs and outputs */ + regressionTemplate(const std::vector > &trainingSet); + /** create with proper models, but not trained */ + regressionTemplate(const int &numInputs, const int &numOutputs); + + /** destructor */ + ~regressionTemplate() {}; + + /** Train on a specified set, causes creation if not created */ + bool train(const std::vector > &trainingSet) override; + + /** Check how far the training has gotten. Averages progress over all models in training */ + float getTrainingProgress(); + + /** Check how many training epochs each model will run. This feature is temporary, and will be replaced by a different design. */ + std::vector getNumEpochs() const; + + /** Call before train, to set the number of training epochs */ + void setNumEpochs(const size_t &epochs); + + /** Check how many hidden layers are in each model. This feature is temporary, and will be replaced by a different design. */ + std::vector getNumHiddenLayers() const; + + /** Set how many hidden layers are in all models. This feature is temporary, and will be replaced by a different design. */ + void setNumHiddenLayers(const int &num_hidden_layers); + + /** Check how many hidden nodes are in each model. This feature is temporary, and will be replaced by a different design. */ + std::vector getNumHiddenNodes() const; + + /** Set how many hidden layers are in all models. This feature is temporary, and will be replaced by a different design. */ + void setNumHiddenNodes(const int &num_hidden_nodes); + private: - - size_t numHiddenLayers; //Temporary -- this should be part of the nn class. -mz - size_t numEpochs; //Temporary -- also should be part of nn only. -mz - size_t numHiddenNodes; //Temporary -- also should be part of nn only. -mz - bool created; + int numHiddenLayers; //Temporary -- this should be part of the nn class. -mz + size_t numEpochs; //Temporary -- also should be part of nn only. -mz + int numHiddenNodes; //Temporary -- also should be part of nn only. -mz + bool created; }; namespace rapidLib { -//This is here so the old API still works -using regression = regressionTemplate; -using regressionFloat = regressionTemplate; + //This is here so the old API still works + using regression = regressionTemplate; + using regressionFloat = regressionTemplate; }; #endif diff --git a/src/seriesClassification.cpp b/src/seriesClassification.cpp index 0c98b714..0716fa55 100644 --- a/src/seriesClassification.cpp +++ b/src/seriesClassification.cpp @@ -20,354 +20,338 @@ #define SEARCH_RADIUS 1 template -seriesClassificationTemplate::seriesClassificationTemplate() {}; +seriesClassificationTemplate::seriesClassificationTemplate() : hopSize(1), counter(0), isTraining(false) {}; template seriesClassificationTemplate::~seriesClassificationTemplate() {}; template -bool seriesClassificationTemplate::train(const std::vector > &seriesSet) +bool seriesClassificationTemplate::train(const std::vector > &seriesSet) { - bool success { false }; - - if (isTraining) - { - throw std::runtime_error("model already training"); - } - else if (seriesSet.size() <= 0) - { - throw std::length_error("training on empty training set."); - } - else - { - isTraining = true; - reset(); - vectorLength = seriesSet[0].input[0].size(); //TODO: check that all vectors are the same size - allTrainingSeries = seriesSet; - minLength = maxLength = allTrainingSeries[0].input.size(); - - for (auto trainingSeries : allTrainingSeries) + bool success = false; + if (isTraining) { - //Global - const size_t newLength { trainingSeries.input.size() }; - if (newLength < minLength) minLength = newLength; - if (newLength > maxLength) maxLength = newLength; - - //Per Label - typename std::map >::iterator it { lengthsPerLabel.find(trainingSeries.label) }; - if (it != lengthsPerLabel.end()) - { - std::size_t newLength { trainingSeries.input.size() }; - if (newLength < it->second.min) it->second.min = static_cast(newLength); - if (newLength > it->second.max) it->second.max = static_cast(newLength); - } - else - { - minMax tempLengths; - tempLengths.min = tempLengths.max = trainingSeries.input.size(); - lengthsPerLabel[trainingSeries.label] = tempLengths; - } + throw std::runtime_error("model already training"); } - //TODO: make this size smarter? - std::vector zeroVector; - for (int i = 0; i < vectorLength; ++i) + else if (seriesSet.size() <= 0) { - zeroVector.push_back(0.0); + throw std::length_error("training on empty training set."); } - for (int i = 0; i < minLength; ++i) + else { - seriesBuffer.push_back(zeroVector); //set size of continuous buffer + isTraining = true; + reset(); + vectorLength = seriesSet[0].input[0].size(); //TODO: check that all vectors are the same size + allTrainingSeries = seriesSet; + minLength = maxLength = allTrainingSeries[0].input.size(); + + //for (size_t i = 0; i < allTrainingSeries.size(); ++i) + for (auto trainingSeries : allTrainingSeries) + { + //for (auto trainingSeries : allTrainingSeries) + //Global + size_t newLength = trainingSeries.input.size(); + if (newLength < minLength) minLength = newLength; + if (newLength > maxLength) maxLength = newLength; + + //Per Label + typename std::map >::iterator it = lengthsPerLabel.find(trainingSeries.label); + if (it != lengthsPerLabel.end()) + { + size_t newLength = trainingSeries.input.size(); + if (newLength < it->second.min) it->second.min = newLength; + if (newLength > it->second.max) it->second.max = newLength; + } + else + { + minMax tempLengths; + tempLengths.min = tempLengths.max = trainingSeries.input.size(); + lengthsPerLabel[trainingSeries.label] = tempLengths; + } + } + //TODO: make this size smarter? + std::vector zeroVector; + for (int i = 0; i < vectorLength; ++i) + { + zeroVector.push_back(0.0); + } + for (int i = 0; i < minLength; ++i) + { + seriesBuffer.push_back(zeroVector); //set size of continuous buffer + } + isTraining = false; + success = true; } - isTraining = false; - success = true; - } - return success; + return success; }; template -void seriesClassificationTemplate::reset() +void seriesClassificationTemplate::reset() { - allCosts.clear(); - allTrainingSeries.clear(); - lengthsPerLabel.clear(); - minLength = -1; - maxLength = -1; - isTraining = false; + allCosts.clear(); + allTrainingSeries.clear(); + lengthsPerLabel.clear(); + minLength = -1; + maxLength = -1; + isTraining = false; } template std::string seriesClassificationTemplate::run(const std::vector>& inputSeries) { - std::string returnLabel { "none" }; - - if (isTraining) - { - throw std::runtime_error("can't run a model during training"); - } - else if (allTrainingSeries.size() > 0) - { - allCosts.clear(); - - T lowestCost = fastDTW::getCost(inputSeries, allTrainingSeries[0].input, SEARCH_RADIUS); - allCosts.push_back(lowestCost); - - for (size_t i { 1 }; i < allTrainingSeries.size(); ++i) + std::string returnLabel = "none"; + if (isTraining) { - T currentCost = fastDTW::getCost(inputSeries, allTrainingSeries[i].input, SEARCH_RADIUS); - allCosts.push_back(currentCost); - - if (currentCost < lowestCost) lowestCost = currentCost; + throw std::runtime_error("can't run a model during training"); } - returnLabel = allTrainingSeries[findClosestSeries()].label; - } - return returnLabel; + else if (allTrainingSeries.size() > 0) + { + size_t closestSeries = 0; + allCosts.clear(); + T lowestCost = fastDTW::getCost(inputSeries, allTrainingSeries[0].input, SEARCH_RADIUS); + allCosts.push_back(lowestCost); + + for (size_t i = 1; i < allTrainingSeries.size(); ++i) { + T currentCost = fastDTW::getCost(inputSeries, allTrainingSeries[i].input, SEARCH_RADIUS); + allCosts.push_back(currentCost); + if (currentCost < lowestCost) { + lowestCost = currentCost; + closestSeries = i; + } + } + returnLabel = allTrainingSeries[findClosestSeries()].label; + } + return returnLabel; }; template T seriesClassificationTemplate::run(const std::vector>& inputSeries, std::string label) { - T lowestCost { std::numeric_limits::max() }; - - if (isTraining) - { - throw std::runtime_error("can't run a model during training"); - } - else - { - allCosts.clear(); - - for (std::size_t i {}; i < allTrainingSeries.size(); ++i) + T returnValue = 0; + if (isTraining) + { + throw std::runtime_error("can't run a model during training"); + } + else { - - if (allTrainingSeries[i].label == label) - { - T currentCost = fastDTW::getCost(inputSeries, allTrainingSeries[i].input, SEARCH_RADIUS); - allCosts.push_back(currentCost); - - if (currentCost < lowestCost) lowestCost = currentCost; - } + int closestSeries = 0; + allCosts.clear(); + T lowestCost = std::numeric_limits::max(); + for (int i = 0; i < allTrainingSeries.size(); ++i) { + if (allTrainingSeries[i].label == label) { + T currentCost = fastDTW::getCost(inputSeries, allTrainingSeries[i].input, SEARCH_RADIUS); + allCosts.push_back(currentCost); + if (currentCost < lowestCost) { + lowestCost = currentCost; + closestSeries = i; + } + } + } + returnValue = lowestCost; } - } - return lowestCost; + return returnValue; }; template -std::string seriesClassificationTemplate::runParallel(const std::vector> &inputSeries) +std::string seriesClassificationTemplate::runParallel(const std::vector> &inputSeries) { - std::string returnLabel{ "none" }; - - if (isTraining) - { - throw std::runtime_error("can't run a model during training"); - } - else - { - allCosts.clear(); - std::vector runningThreads {}; - - for (std::size_t i {}; i < allTrainingSeries.size(); ++i) + std::string returnLabel = "none"; + if (isTraining) { - runningThreads.push_back(std::thread(&seriesClassificationTemplate::runThread, this, inputSeries, i)); + throw std::runtime_error("can't run a model during training"); } - - for (std::size_t i {}; i < allTrainingSeries.size(); ++i) + else { - runningThreads.at(i).join(); + allCosts.clear(); + std::vector runningThreads; + for (std::size_t i = 0; i < allTrainingSeries.size(); ++i) + { + runningThreads.push_back(std::thread(&seriesClassificationTemplate::runThread, this, inputSeries, i)); + } + for (std::size_t i = 0; i < allTrainingSeries.size(); ++i) + { + runningThreads.at(i).join(); + } + returnLabel = allTrainingSeries[findClosestSeries()].label; } - - returnLabel = allTrainingSeries[findClosestSeries()].label; - } - - return returnLabel; + + return returnLabel; }; template T seriesClassificationTemplate::runParallel(const std::vector> &inputSeries, std::string label) { - T returnValue { 0 }; - - if (isTraining) - { - throw std::runtime_error("can't run a model during training"); - } - else - { - allCosts.clear(); - std::vector runningThreads {}; - int seriesIndex {}; - - for (const auto series : allTrainingSeries) + T returnValue = 0; + if (isTraining) { - if (series.label == label) - { - runningThreads.push_back(std::thread(&seriesClassificationTemplate::runThread, this, inputSeries, seriesIndex)); - ++seriesIndex; - } + throw std::runtime_error("can't run a model during training"); } - - for (std::size_t i {}; i < runningThreads.size(); ++i) + else { - runningThreads.at(i).join(); //FIXME: not sure what's up here... + allCosts.clear(); + std::vector runningThreads; + int seriesIndex; + for (std::size_t i = 0; i < allTrainingSeries.size(); ++i) + { + if (allTrainingSeries[i].label == label) + { + runningThreads.push_back(std::thread(&seriesClassificationTemplate::runThread, this, inputSeries, seriesIndex)); + ++seriesIndex; + } + } + for (std::size_t i = 0; i < runningThreads.size(); ++i) + { + runningThreads.at(i).join(); //FIXME: not sure what's up here... + } + returnValue = allCosts.at(findClosestSeries()); } - - returnValue = allCosts.at(findClosestSeries()); - } - - return returnValue; + return returnValue; }; template -size_t seriesClassificationTemplate::findClosestSeries() const +size_t seriesClassificationTemplate::findClosestSeries() const { - T lowestCost { allCosts[0] }; - - size_t closestSeries {}; - - for (std::size_t i { 1 }; i < allCosts.size(); ++i) - { - if (allCosts[i] < lowestCost) + T lowestCost = allCosts[0]; + size_t closestSeries = 0; + for (std::size_t i = 1; i < allCosts.size(); ++i) { - lowestCost = allCosts[i]; - closestSeries = i; + if (allCosts[i] < lowestCost) + { + lowestCost = allCosts[i]; + closestSeries = i; + } } - } - - return closestSeries; + return closestSeries; } template -void seriesClassificationTemplate::runThread(const std::vector> &inputSeries, std::size_t i) +void seriesClassificationTemplate::runThread(const std::vector> &inputSeries, std::size_t i) { - allCosts.push_back(std::numeric_limits::max()); //initialized cost - allCosts[i] = fastDTW::getCost(inputSeries, allTrainingSeries[i].input, SEARCH_RADIUS); + allCosts.push_back(std::numeric_limits::max()); //initialized cost + allCosts[i] = fastDTW::getCost(inputSeries, allTrainingSeries[i].input, SEARCH_RADIUS); } template -std::string seriesClassificationTemplate::runContinuous(const std::vector &inputVector) +std::string seriesClassificationTemplate::runContinuous(const std::vector &inputVector) { - seriesBuffer.erase(seriesBuffer.begin()); - seriesBuffer.push_back(inputVector); - std::string returnString { "none" }; - - if ((counter % hopSize) == 0 ) - { - if (isTraining) throw std::runtime_error("can't run a model during training"); - - returnString = run(seriesBuffer); //TODO: Have an option to run parallel here. - counter = 0; - } - - ++counter; - return returnString; + seriesBuffer.erase(seriesBuffer.begin()); + seriesBuffer.push_back(inputVector); + std::string returnString = "none"; + if ((counter % hopSize) == 0 ) + { + if (isTraining) + { + throw std::runtime_error("can't run a model during training"); + } + returnString = run(seriesBuffer); //TODO: Have an option to run parallel here. + counter = 0; + } + ++counter; + return returnString; } template std::vector seriesClassificationTemplate::getCosts() const { - return allCosts; + return allCosts; } template std::size_t seriesClassificationTemplate::getMinLength() const { - return minLength; + return minLength; } template -std::size_t seriesClassificationTemplate::getMinLength(std::string label) const +std::size_t seriesClassificationTemplate::getMinLength(std::string label) const { - std::size_t labelMinLength {}; - typename std::map >::const_iterator it { lengthsPerLabel.find(label)}; - - if (it != lengthsPerLabel.end()) labelMinLength = it->second.min; - - return labelMinLength; + std::size_t labelMinLength = -1; + typename std::map >::const_iterator it = lengthsPerLabel.find(label); + if (it != lengthsPerLabel.end()) labelMinLength = it->second.min; + return labelMinLength; } template -std::size_t seriesClassificationTemplate::getMaxLength() const +std::size_t seriesClassificationTemplate::getMaxLength() const { - return maxLength; + return maxLength; } template -std::size_t seriesClassificationTemplate::getMaxLength(std::string label) const +std::size_t seriesClassificationTemplate::getMaxLength(std::string label) const { - std::size_t labelMaxLength {}; - typename std::map >::const_iterator it { lengthsPerLabel.find(label) }; - - if (it != lengthsPerLabel.end()) labelMaxLength = it->second.max; - - return labelMaxLength; + std::size_t labelMaxLength = -1; + typename std::map >::const_iterator it = lengthsPerLabel.find(label); + if (it != lengthsPerLabel.end()) labelMaxLength = it->second.max; + return labelMaxLength; } template -typename seriesClassificationTemplate::template minMax seriesClassificationTemplate::calculateCosts(std::string label) const +typename seriesClassificationTemplate::template minMax seriesClassificationTemplate::calculateCosts(std::string label) const { - minMax calculatedMinMax {}; - bool foundSeries { false }; - std::vector labelCosts {}; - - for (size_t i {}; i < (allTrainingSeries.size() - 1); ++i) //these loops are a little different than the two-label case - { - if (allTrainingSeries[i].label == label) - { - foundSeries = true; - for (size_t j { i + 1 }; j < allTrainingSeries.size(); ++j) - { - if (allTrainingSeries[j].label == label) + minMax calculatedMinMax; + bool foundSeries = false; + std::vector labelCosts; + + for (size_t i = 0; i < (allTrainingSeries.size() - 1); ++i) //these loops are a little different than the two-label case + { + if (allTrainingSeries[i].label == label) { - labelCosts.push_back(fastDTW::getCost(allTrainingSeries[i].input, allTrainingSeries[j].input, SEARCH_RADIUS)); + foundSeries = true; + for (size_t j = (i + 1); j < allTrainingSeries.size(); ++j) + { + if (allTrainingSeries[j].label == label) + { + labelCosts.push_back(fastDTW::getCost(allTrainingSeries[i].input, allTrainingSeries[j].input, SEARCH_RADIUS)); + } + } } - } } - } - - if (foundSeries) - { - auto minmax_result { std::minmax_element(std::begin(labelCosts), std::end(labelCosts)) }; - calculatedMinMax.min = *minmax_result.first; - calculatedMinMax.max = *minmax_result.second; - } - else - { - calculatedMinMax.min = calculatedMinMax.max = 0; - } - - return calculatedMinMax; + + if (foundSeries) + { + auto minmax_result = std::minmax_element(std::begin(labelCosts), std::end(labelCosts)); + calculatedMinMax.min = *minmax_result.first; + calculatedMinMax.max = *minmax_result.second; + } + else + { + calculatedMinMax.min = calculatedMinMax.max = 0; + } + return calculatedMinMax; } template typename seriesClassificationTemplate::template minMax seriesClassificationTemplate::calculateCosts(std::string label1, std::string label2) const { - minMax calculatedMinMax; - bool foundSeries = false; - std::vector labelCosts; - - for (size_t i = 0; i < (allTrainingSeries.size()); ++i) - { - if (allTrainingSeries[i].label == label1) { - for (size_t j = 0; j < allTrainingSeries.size(); ++j) - { - if (allTrainingSeries[j].label == label2) - { - foundSeries = true; - labelCosts.push_back(fastDTW::getCost(allTrainingSeries[i].input, allTrainingSeries[j].input, SEARCH_RADIUS)); + minMax calculatedMinMax; + bool foundSeries = false; + std::vector labelCosts; + + for (size_t i = 0; i < (allTrainingSeries.size()); ++i) + { + if (allTrainingSeries[i].label == label1) { + for (size_t j = 0; j < allTrainingSeries.size(); ++j) + { + if (allTrainingSeries[j].label == label2) + { + foundSeries = true; + labelCosts.push_back(fastDTW::getCost(allTrainingSeries[i].input, allTrainingSeries[j].input, SEARCH_RADIUS)); + } + } } - } } - } - - if (foundSeries) - { - auto minmax_result = std::minmax_element(std::begin(labelCosts), std::end(labelCosts)); - calculatedMinMax.min = *minmax_result.first; - calculatedMinMax.max = *minmax_result.second; - } - else - { - calculatedMinMax.min = calculatedMinMax.max = 0; - } - return calculatedMinMax; + + if (foundSeries) + { + auto minmax_result = std::minmax_element(std::begin(labelCosts), std::end(labelCosts)); + calculatedMinMax.min = *minmax_result.first; + calculatedMinMax.max = *minmax_result.second; + } + else + { + calculatedMinMax.min = calculatedMinMax.max = 0; + } + return calculatedMinMax; } //explicit instantiation @@ -376,7 +360,7 @@ template class seriesClassificationTemplate; // -//std::vector seriesClassification::getCosts(const std::vector &trainingSet) +//std::vector seriesClassification::getCosts(const std::vector &trainingSet) //{ // run(trainingSet); // return allCosts; diff --git a/src/seriesClassification.h b/src/seriesClassification.h index e8369002..1c1de4bf 100644 --- a/src/seriesClassification.h +++ b/src/seriesClassification.h @@ -25,121 +25,120 @@ template class seriesClassificationTemplate final { public: - - /** Constructor, no params */ - seriesClassificationTemplate(); - ~seriesClassificationTemplate(); - - /** Train on a specified set of trainingSeries - * @param std::vector A vector of training series - */ - bool train(const std::vector > &seriesSet); - - /** Reset model to its initial state, forget all costs and training data*/ - void reset(); - - /** Compare an input series to the stored training series - * @param std::vector vector of vectors, either float or double input data - * @return The label of the closest training series. - */ - std::string run(const std::vector > &inputSeries); - - /** Compare an input series to all of the stored series with a specified label - * @param std::vector either float or double input data - * @param String label to compare with - * @return The lowest cost match, float or double - */ - T run(const std::vector >& inputSeries, std::string label); - - /** Compare an input series to the stored training series. Parallel processing - * @param std::vector vector of vectors, either float or double input data - * @return The label of the closest training series. - */ - std::string runParallel(const std::vector >& inputSeries); - - /** Compare an input series to all of the stored series with a specified label. Parallel processing - * @param std::vector either float or double input data - * @param String label to compare with - * @return The lowest cost match, float or double - */ - T runParallel(const std::vector > &inputSeries, std::string label); - - /** Compare an input series to all of the stored series with a specified label - * @param std::vector one frame either float or double input data - * @return The lowest cost match, float or double - */ - std::string runContinuous(const std::vector &inputVector); - - /** Get the costs that were calculated by the run method - * @return A vector of floats or doubles, the cost of matching to each training series - */ - std::vector getCosts() const; - - /** Get minimum training series length - * @return The minimum length training series - */ - std::size_t getMinLength() const; - - /** Get minimum training series length from a specified label - * @param string The label to check - * @return The minimum length training series of that label - */ - std::size_t getMinLength(std::string label) const; - - /** Get maximum training series length - * @return The maximum length training series - */ - std::size_t getMaxLength() const; - - /** Get maximum training series length from a specified label - * @param string The label to check - * @return The maximum length training series of that label - */ - std::size_t getMaxLength(std::string label) const; - - /** Return struct for calculate costs */ - template - struct minMax { - TT min; - TT max; - }; - - /** Calculate minimum and maximum cost between examples in a label. - * @param string Label to calculate - * @return minMax struct containing min and max - */ - minMax calculateCosts(std::string label) const; - - /** Calculate minimum and maximum cost between examples in one label and examples in a second. - * @param string first label to compare - * @param string second label to compare - * @return minMax struct containing min and max - */ - minMax calculateCosts(std::string label1, std::string label2) const; - + + /** Constructor, no params */ + seriesClassificationTemplate(); + ~seriesClassificationTemplate(); + + /** Train on a specified set of trainingSeries + * @param std::vector A vector of training series + */ + bool train(const std::vector > &seriesSet); + + /** Reset model to its initial state, forget all costs and training data*/ + void reset(); + + /** Compare an input series to the stored training series + * @param std::vector vector of vectors, either float or double input data + * @return The label of the closest training series. + */ + std::string run(const std::vector > &inputSeries); + + /** Compare an input series to all of the stored series with a specified label + * @param std::vector either float or double input data + * @param String label to compare with + * @return The lowest cost match, float or double + */ + T run(const std::vector >& inputSeries, std::string label); + + /** Compare an input series to the stored training series. Parallel processing + * @param std::vector vector of vectors, either float or double input data + * @return The label of the closest training series. + */ + std::string runParallel(const std::vector >& inputSeries); + + /** Compare an input series to all of the stored series with a specified label. Parallel processing + * @param std::vector either float or double input data + * @param String label to compare with + * @return The lowest cost match, float or double + */ + T runParallel(const std::vector > &inputSeries, std::string label); + + /** Compare an input series to all of the stored series with a specified label + * @param std::vector one frame either float or double input data + * @return The lowest cost match, float or double + */ + std::string runContinuous(const std::vector &inputVector); + + /** Get the costs that were calculated by the run method + * @return A vector of floats or doubles, the cost of matching to each training series + */ + std::vector getCosts() const; + + /** Get minimum training series length + * @return The minimum length training series + */ + std::size_t getMinLength() const; + + /** Get minimum training series length from a specified label + * @param string The label to check + * @return The minimum length training series of that label + */ + std::size_t getMinLength(std::string label) const; + + /** Get maximum training series length + * @return The maximum length training series + */ + std::size_t getMaxLength() const; + + /** Get maximum training series length from a specified label + * @param string The label to check + * @return The maximum length training series of that label + */ + std::size_t getMaxLength(std::string label) const; + + /** Return struct for calculate costs */ + template + struct minMax { + TT min; + TT max; + }; + + /** Calculate minimum and maximum cost between examples in a label. + * @param string Label to calculate + * @return minMax struct containing min and max + */ + minMax calculateCosts(std::string label) const; + + /** Calculate minimum and maximum cost between examples in one label and examples in a second. + * @param string first label to compare + * @param string second label to compare + * @return minMax struct containing min and max + */ + minMax calculateCosts(std::string label1, std::string label2) const; + private: - - std::vector > allTrainingSeries; - size_t vectorLength; - std::vector allCosts; - size_t maxLength; - size_t minLength; - std::map > lengthsPerLabel; - bool isTraining { false }; - - std::vector > seriesBuffer; - int hopSize {1}; - int counter {0}; - - size_t findClosestSeries() const; - void runThread(const std::vector> &inputSeries, std::size_t i); + std::vector > allTrainingSeries; + size_t vectorLength; + std::vector allCosts; + size_t maxLength; + size_t minLength; + std::map > lengthsPerLabel; + bool isTraining; + + std::vector > seriesBuffer; + int hopSize; + int counter; + + size_t findClosestSeries() const; + void runThread(const std::vector> &inputSeries, std::size_t i); }; namespace rapidLib { -//This is here to keep the old API working -using seriesClassification = seriesClassificationTemplate; -using seriesClassificationFloat = seriesClassificationTemplate; + //This is here to keep the old API working + using seriesClassification = seriesClassificationTemplate; + using seriesClassificationFloat = seriesClassificationTemplate; } #endif