Auto reformatted the base, ref #470

This commit is contained in:
emeric
2024-05-24 23:31:52 +02:00
parent 83b868673c
commit 39941d90a3
460 changed files with 8583 additions and 8514 deletions
+2 -1
View File
@@ -18,6 +18,7 @@
*/
#include <random>
#include <benchmark/benchmark.h>
#include "som/Network.hpp"
@@ -50,6 +51,6 @@ namespace lms::som
// Register the benchmark with custom range
BENCHMARK(BM_Matrix)->Arg(3)->Arg(6)->Arg(12)->Arg(24);
}
} // namespace lms::som
BENCHMARK_MAIN();
+65 -76
View File
@@ -25,96 +25,85 @@
namespace lms::som
{
template<typename T>
static T variance(const std::vector<T>& vec)
{
std::size_t size{ vec.size() };
template<typename T>
static
T
variance(const std::vector<T>& vec)
{
std::size_t size {vec.size()};
if (size == 1)
return T{};
if (size == 1)
return T {};
const T mean{ std::accumulate(vec.begin(), vec.end(), T{}) / size };
const T mean {std::accumulate(vec.begin(), vec.end(), T{}) / size};
return std::accumulate(vec.begin(), vec.end(), T{},
[mean, size](T accumulator, const T& val) {
return accumulator + ((val - mean) * (val - mean) / (size - 1));
});
}
return std::accumulate(vec.begin(), vec.end(), T {},
[mean, size] (T accumulator, const T& val)
{
return accumulator + ((val - mean) * (val - mean) / (size - 1));
});
}
DataNormalizer::DataNormalizer(std::size_t inputDimCount)
: _inputDimCount{ inputDimCount }
{
}
DataNormalizer::DataNormalizer(std::size_t inputDimCount)
: _inputDimCount{inputDimCount}
{
}
const DataNormalizer::MinMax& DataNormalizer::getValue(std::size_t index) const
{
return _minmax[index];
}
const DataNormalizer::MinMax&
DataNormalizer::getValue(std::size_t index) const
{
return _minmax[index];
}
void DataNormalizer::setValue(std::size_t index, const MinMax& minMax)
{
_minmax[index] = minMax;
}
void
DataNormalizer::setValue(std::size_t index, const MinMax& minMax)
{
_minmax[index] = minMax;
}
void DataNormalizer::computeNormalizationFactors(const std::vector<InputVector>& inputVectors)
{
if (inputVectors.empty())
throw Exception("Empty input vectors");
void
DataNormalizer::computeNormalizationFactors(const std::vector<InputVector>& inputVectors)
{
if (inputVectors.empty())
throw Exception("Empty input vectors");
// For each dimension of the input, compute the min/max
_minmax.clear();
_minmax.resize(_inputDimCount);
// For each dimension of the input, compute the min/max
_minmax.clear();
_minmax.resize(_inputDimCount);
for (std::size_t dimId{}; dimId < _inputDimCount; ++dimId)
{
std::vector<InputVector::value_type> values;
for (std::size_t dimId {}; dimId < _inputDimCount; ++dimId)
{
std::vector<InputVector::value_type> values;
for (const auto& inputVector : inputVectors)
{
checkSameDimensions(inputVector, _inputDimCount);
values.push_back(inputVector[dimId]);
}
for (const auto& inputVector: inputVectors)
{
checkSameDimensions(inputVector, _inputDimCount);
values.push_back(inputVector[dimId]);
}
auto result{ std::minmax_element(values.begin(), values.end()) };
_minmax[dimId] = { *result.first, *result.second };
}
}
auto result {std::minmax_element(values.begin(), values.end())};
_minmax[dimId] = {*result.first, *result.second};
}
}
InputVector::value_type DataNormalizer::normalizeValue(InputVector::value_type value, std::size_t dimId) const
{
// clamp
if (value > _minmax[dimId].max)
value = _minmax[dimId].max;
else if (value < _minmax[dimId].min)
value = _minmax[dimId].min;
InputVector::value_type
DataNormalizer::normalizeValue(InputVector::value_type value, std::size_t dimId) const
{
// clamp
if (value > _minmax[dimId].max)
value = _minmax[dimId].max;
else if (value < _minmax[dimId].min)
value = _minmax[dimId].min;
return (value - _minmax[dimId].min) / (_minmax[dimId].max - _minmax[dimId].min);
}
return (value - _minmax[dimId].min) / (_minmax[dimId].max - _minmax[dimId].min);
}
void DataNormalizer::normalizeData(InputVector& a) const
{
checkSameDimensions(a, _inputDimCount);
void
DataNormalizer::normalizeData(InputVector& a) const
{
checkSameDimensions(a, _inputDimCount);
for (std::size_t dimId {}; dimId < _inputDimCount; ++dimId)
{
a[dimId] = normalizeValue(a[dimId], dimId);
}
}
void
DataNormalizer::dump(std::ostream& os) const
{
for (std::size_t i {}; i < _inputDimCount; ++i)
os << "(" << _minmax[i].min << ", " << _minmax[i].max << ")";
}
for (std::size_t dimId{}; dimId < _inputDimCount; ++dimId)
{
a[dimId] = normalizeValue(a[dimId], dimId);
}
}
void DataNormalizer::dump(std::ostream& os) const
{
for (std::size_t i{}; i < _inputDimCount; ++i)
os << "(" << _minmax[i].min << ", " << _minmax[i].max << ")";
}
} // namespace lms::som
+214 -244
View File
@@ -31,300 +31,270 @@
namespace lms::som
{
void checkSameDimensions(const InputVector& a, const InputVector& b)
{
if (!a.hasSameDimension(b))
throw Exception("Bad data dimension count");
}
void
checkSameDimensions(const InputVector& a, const InputVector& b)
{
if (!a.hasSameDimension(b))
throw Exception("Bad data dimension count");
}
void checkSameDimensions(const InputVector& a, std::size_t inputDimCount)
{
if (a.getNbDimensions() != inputDimCount)
throw Exception("Bad data dimension count");
}
void
checkSameDimensions(const InputVector& a, std::size_t inputDimCount)
{
if (a.getNbDimensions() != inputDimCount)
throw Exception("Bad data dimension count");
}
static LearningFactor defaultLearningFactor(Network::CurrentIteration iteration)
{
static const LearningFactor initialValue{ 1 };
static LearningFactor
defaultLearningFactor(Network::CurrentIteration iteration)
{
static const LearningFactor initialValue{1};
return initialValue * exp(-((iteration.idIteration + 1) / static_cast<LearningFactor>(iteration.iterationCount)));
}
return initialValue * exp(-((iteration.idIteration + 1) / static_cast<LearningFactor>(iteration.iterationCount)));
}
static InputVector::Distance euclidianSquareDistance(const InputVector& a, const InputVector& b, const InputVector& weights)
{
return a.computeEuclidianSquareDistance(b, weights);
}
static InputVector::Distance
euclidianSquareDistance(const InputVector& a, const InputVector& b, const InputVector& weights)
{
return a.computeEuclidianSquareDistance(b, weights);
}
static InputVector::value_type sigmaFunc(Network::CurrentIteration iteration)
{
constexpr InputVector::value_type sigma0{ 1 };
static
InputVector::value_type
sigmaFunc(Network::CurrentIteration iteration)
{
constexpr InputVector::value_type sigma0 {1};
return sigma0 * std::exp(-((iteration.idIteration + 1) / static_cast<InputVector::value_type>(iteration.iterationCount)));
}
return sigma0 * std::exp(- ((iteration.idIteration + 1) / static_cast<InputVector::value_type>(iteration.iterationCount)));
}
static InputVector::value_type defaultNeighbourhoodFunc(Norm norm, const Network::CurrentIteration& iteration)
{
InputVector::value_type sigma{ sigmaFunc(iteration) };
static
InputVector::value_type
defaultNeighbourhoodFunc(Norm norm, const Network::CurrentIteration& iteration)
{
InputVector::value_type sigma {sigmaFunc(iteration)};
return exp(-norm / (2 * sigma * sigma));
}
return exp(-norm / (2 * sigma * sigma));
}
Network::Network(Coordinate width, Coordinate height, std::size_t inputDimCount)
: _inputDimCount{ inputDimCount }
, _weights{ inputDimCount, static_cast<InputVector::value_type>(1) }
, _refVectors{ width, height, _inputDimCount }
, _distanceFunc{ euclidianSquareDistance }
, _learningFactorFunc{ defaultLearningFactor }
, _neighbourhoodFunc{ defaultNeighbourhoodFunc }
{
// init each vector with a random normalized value
for (Coordinate y{}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x{}; x < _refVectors.getWidth(); ++x)
{
for (InputVector::value_type& val : _refVectors.get({ x, y }))
val = core::random::getRealRandom<InputVector::value_type>(0, 1);
}
}
}
Network::Network(Coordinate width, Coordinate height, std::size_t inputDimCount)
:
_inputDimCount {inputDimCount},
_weights {inputDimCount, static_cast<InputVector::value_type>(1)},
_refVectors {width, height, _inputDimCount},
_distanceFunc {euclidianSquareDistance},
_learningFactorFunc {defaultLearningFactor},
_neighbourhoodFunc {defaultNeighbourhoodFunc}
{
// init each vector with a random normalized value
for (Coordinate y {}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x {}; x < _refVectors.getWidth(); ++x)
{
for (InputVector::value_type& val : _refVectors.get({x,y}))
val = core::random::getRealRandom<InputVector::value_type>(0, 1);
}
}
}
void Network::setDataWeights(const InputVector& weights)
{
checkSameDimensions(weights, _inputDimCount);
void
Network::setDataWeights(const InputVector& weights)
{
checkSameDimensions(weights, _inputDimCount);
_weights = weights;
}
_weights = weights;
}
void Network::setRefVector(const Position& position, const InputVector& data)
{
checkSameDimensions(data, _inputDimCount);
void
Network::setRefVector(const Position& position, const InputVector& data)
{
checkSameDimensions(data, _inputDimCount);
_refVectors[position] = data;
}
_refVectors[position] = data;
}
InputVector::Distance Network::getRefVectorsDistance(const Position& position1, const Position& position2) const
{
return _distanceFunc(_refVectors.get(position1), _refVectors.get(position2), _weights);
}
InputVector::Distance
Network::getRefVectorsDistance(const Position& position1, const Position& position2) const
{
return _distanceFunc(_refVectors.get(position1), _refVectors.get(position2), _weights);
}
InputVector::Distance Network::computeRefVectorsDistanceMean() const
{
std::vector<InputVector::Distance> values;
values.reserve(2 * _refVectors.getHeight() * _refVectors.getWidth() - _refVectors.getWidth() - _refVectors.getHeight());
for (Coordinate y{}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x{}; x < _refVectors.getWidth(); ++x)
{
if (x != _refVectors.getWidth() - 1)
values.emplace_back(getRefVectorsDistance({ x, y }, { x + 1, y }));
if (y != _refVectors.getHeight() - 1)
values.emplace_back(getRefVectorsDistance({ x, y }, { x, y + 1 }));
}
}
InputVector::Distance
Network::computeRefVectorsDistanceMean() const
{
std::vector<InputVector::Distance> values;
values.reserve(2 * _refVectors.getHeight()*_refVectors.getWidth() - _refVectors.getWidth() - _refVectors.getHeight());
for (Coordinate y {}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x {}; x < _refVectors.getWidth(); ++x)
{
if (x != _refVectors.getWidth() - 1)
values.emplace_back(getRefVectorsDistance( {x, y}, {x + 1, y}));
if (y != _refVectors.getHeight() - 1)
values.emplace_back(getRefVectorsDistance( {x, y}, {x, y + 1}));
}
}
return std::accumulate(values.begin(), values.end(), 0.) / values.size();
}
return std::accumulate(values.begin(), values.end(), 0.) / values.size();
}
double Network::computeRefVectorsDistanceMedian() const
{
std::vector<InputVector::Distance> values;
values.reserve(2 * _refVectors.getHeight() * _refVectors.getWidth() - _refVectors.getWidth() - _refVectors.getHeight());
for (Coordinate y{}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x{}; x < _refVectors.getWidth(); ++x)
{
if (x != _refVectors.getWidth() - 1)
values.emplace_back(getRefVectorsDistance({ x, y }, { x + 1, y }));
if (y != _refVectors.getHeight() - 1)
values.emplace_back(getRefVectorsDistance({ x, y }, { x, y + 1 }));
}
}
double
Network::computeRefVectorsDistanceMedian() const
{
std::vector<InputVector::Distance> values;
values.reserve(2*_refVectors.getHeight()*_refVectors.getWidth() - _refVectors.getWidth() - _refVectors.getHeight());
for (Coordinate y {}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x {}; x < _refVectors.getWidth(); ++x)
{
if (x != _refVectors.getWidth() - 1)
values.emplace_back(getRefVectorsDistance( {x, y}, {x + 1, y}));
if (y != _refVectors.getHeight() - 1)
values.emplace_back(getRefVectorsDistance( {x, y}, {x, y + 1}));
}
}
std::sort(values.begin(), values.end());
std::sort(values.begin(), values.end());
return values[values.size() > 1 ? values.size() / 2 - 1 : 0];
}
return values[values.size() > 1 ? values.size()/2 - 1 : 0];
}
void Network::dump(std::ostream& os) const
{
os << "Width: " << _refVectors.getWidth() << ", Height: " << _refVectors.getHeight() << std::endl;
;
void
Network::dump(std::ostream& os) const
{
os << "Width: " << _refVectors.getWidth() << ", Height: " << _refVectors.getHeight() << std::endl;;
for (Coordinate y{}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x{}; x < _refVectors.getWidth(); ++x)
{
os << _refVectors.get({ x, y }) << " ";
}
for (Coordinate y {}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x {}; x < _refVectors.getWidth(); ++x)
{
os << _refVectors.get({x, y}) << " ";
}
os << std::endl;
}
os << std::endl;
}
os << std::endl;
}
os << std::endl;
}
Position Network::getClosestRefVectorPosition(const InputVector& data) const
{
return _refVectors.getPositionMinElement([&](const auto& a, const auto& b) {
return (_distanceFunc(a, data, _weights) < _distanceFunc(b, data, _weights));
});
}
Position
Network::getClosestRefVectorPosition(const InputVector& data) const
{
return _refVectors.getPositionMinElement([&](const auto& a, const auto& b)
{
return (_distanceFunc(a, data, _weights) < _distanceFunc(b, data, _weights));
});
}
std::optional<Position> Network::getClosestRefVectorPosition(const InputVector& data, InputVector::Distance maxDistance) const
{
std::optional<Position> position{ getClosestRefVectorPosition(data) };
std::optional<Position>
Network::getClosestRefVectorPosition(const InputVector& data, InputVector::Distance maxDistance) const
{
std::optional<Position> position {getClosestRefVectorPosition(data)};
if (_distanceFunc(data, _refVectors.get(*position), _weights) > maxDistance)
position.reset();
if (_distanceFunc(data, _refVectors.get(*position), _weights) > maxDistance)
position.reset();
return position;
}
return position;
}
std::optional<Position> Network::getClosestRefVectorPosition(const std::vector<Position>& refVectorsPosition, InputVector::Distance maxDistance) const
{
std::unordered_set<Position> neighboursPosition;
for (const Position& refVectorPosition : refVectorsPosition)
{
if (refVectorPosition.y > 0)
neighboursPosition.insert({ refVectorPosition.x, refVectorPosition.y - 1 });
if (refVectorPosition.y < _refVectors.getHeight() - 1)
neighboursPosition.insert({ refVectorPosition.x, refVectorPosition.y + 1 });
if (refVectorPosition.x > 0)
neighboursPosition.insert({ refVectorPosition.x - 1, refVectorPosition.y });
if (refVectorPosition.x < _refVectors.getWidth() - 1)
neighboursPosition.insert({ refVectorPosition.x + 1, refVectorPosition.y });
}
std::optional<Position>
Network::getClosestRefVectorPosition(const std::vector<Position>& refVectorsPosition, InputVector::Distance maxDistance) const
{
std::unordered_set<Position> neighboursPosition;
for (const Position& refVectorPosition : refVectorsPosition)
{
if (refVectorPosition.y > 0)
neighboursPosition.insert({ refVectorPosition.x, refVectorPosition.y - 1 });
if (refVectorPosition.y < _refVectors.getHeight() - 1)
neighboursPosition.insert({ refVectorPosition.x, refVectorPosition.y + 1 });
if (refVectorPosition.x > 0)
neighboursPosition.insert({ refVectorPosition.x - 1, refVectorPosition.y });
if (refVectorPosition.x < _refVectors.getWidth() - 1)
neighboursPosition.insert({ refVectorPosition.x + 1, refVectorPosition.y });
}
// remove position that are in the input position
for (const auto& refVectorPosition : refVectorsPosition)
neighboursPosition.erase(refVectorPosition);
// remove position that are in the input position
for (const auto& refVectorPosition : refVectorsPosition)
neighboursPosition.erase(refVectorPosition);
if (neighboursPosition.empty())
return std::nullopt;
if (neighboursPosition.empty())
return std::nullopt;
// Now compute the distance for each neighbour
struct NeighbourInfo
{
Position position;
double distance;
};
// Now compute the distance for each neighbour
struct NeighbourInfo
{
Position position;
double distance;
};
std::vector<NeighbourInfo> neighboursInfo;
for (const Position& neighbourPosition : neighboursPosition)
{
auto min = std::min_element(refVectorsPosition.begin(), refVectorsPosition.end(),
[this, neighbourPosition](const auto& a, const auto& b) {
return (this->getRefVectorsDistance(a, neighbourPosition) < this->getRefVectorsDistance(b, neighbourPosition));
});
std::vector<NeighbourInfo> neighboursInfo;
for (const Position& neighbourPosition : neighboursPosition)
{
auto min = std::min_element(refVectorsPosition.begin(), refVectorsPosition.end(),
[this, neighbourPosition](const auto& a, const auto& b)
{
return (this->getRefVectorsDistance(a, neighbourPosition) < this->getRefVectorsDistance(b, neighbourPosition));
});
InputVector::Distance distance{ getRefVectorsDistance(neighbourPosition, *min) };
if (distance > maxDistance)
continue;
InputVector::Distance distance {getRefVectorsDistance(neighbourPosition, *min)};
if (distance > maxDistance)
continue;
neighboursInfo.emplace_back(NeighbourInfo{ neighbourPosition, distance });
}
neighboursInfo.emplace_back(NeighbourInfo {neighbourPosition, distance});
}
if (neighboursInfo.empty())
return std::nullopt;
if (neighboursInfo.empty())
return std::nullopt;
auto min{ std::min_element(std::cbegin(neighboursInfo), std::cend(neighboursInfo),
[&](const auto& a, const auto& b) {
return a.distance < b.distance;
}) };
auto min {std::min_element(std::cbegin(neighboursInfo), std::cend(neighboursInfo),
[&](const auto& a, const auto& b)
{
return a.distance < b.distance;
})};
return min->position;
}
static Norm computePositionNorm(const Position& c1, const Position& c2)
{
return std::sqrt((c1.x - c2.x) * (c1.x - c2.x) + (c1.y - c2.y) * (c1.y - c2.y));
}
return min->position;
}
void Network::updateRefVectors(const Position& closestRefVectorPosition, const InputVector& input, LearningFactor learningFactor, const CurrentIteration& iteration)
{
for (Coordinate y{}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x{}; x < _refVectors.getWidth(); ++x)
{
InputVector& refVector{ _refVectors.get({ x, y }) };
static Norm
computePositionNorm(const Position& c1, const Position& c2)
{
return std::sqrt((c1.x - c2.x) * (c1.x - c2.x) + (c1.y - c2.y) * (c1.y - c2.y));
}
const Norm norm{ computePositionNorm({ x, y }, closestRefVectorPosition) };
void
Network::updateRefVectors(const Position& closestRefVectorPosition, const InputVector& input, LearningFactor learningFactor, const CurrentIteration& iteration)
{
for (Coordinate y {}; y < _refVectors.getHeight(); ++y)
{
for (Coordinate x {}; x < _refVectors.getWidth(); ++x)
{
InputVector& refVector {_refVectors.get({x, y})};
InputVector delta{ input - refVector };
delta *= (learningFactor * _neighbourhoodFunc(norm, iteration));
const Norm norm {computePositionNorm({x, y}, closestRefVectorPosition)};
refVector += delta;
}
}
}
InputVector delta {input - refVector};
delta *= (learningFactor * _neighbourhoodFunc(norm, iteration));
void Network::train(const std::vector<InputVector>& inputData, std::size_t nbIterations, ProgressCallback progressCallback, RequestStopCallback requestStopCallback)
{
bool stopRequested{ false };
std::vector<const InputVector*> inputDataShuffled;
refVector += delta;
}
}
}
inputDataShuffled.reserve(inputData.size());
for (const auto& input : inputData)
inputDataShuffled.push_back(&input);
void
Network::train(const std::vector<InputVector>& inputData, std::size_t nbIterations, ProgressCallback progressCallback, RequestStopCallback requestStopCallback)
{
bool stopRequested {false};
std::vector<const InputVector*> inputDataShuffled;
for (std::size_t i{}; i < nbIterations; ++i)
{
CurrentIteration curIter{ i, nbIterations };
inputDataShuffled.reserve(inputData.size());
for (const auto& input : inputData)
inputDataShuffled.push_back(&input);
if (progressCallback)
progressCallback(curIter);
for (std::size_t i {}; i < nbIterations; ++i)
{
CurrentIteration curIter {i, nbIterations};
core::random::shuffleContainer(inputDataShuffled);
if (progressCallback)
progressCallback(curIter);
const LearningFactor learningFactor{ _learningFactorFunc(curIter) };
core::random::shuffleContainer(inputDataShuffled);
for (const InputVector* input : inputDataShuffled)
{
if (requestStopCallback)
stopRequested = requestStopCallback();
const LearningFactor learningFactor {_learningFactorFunc(curIter)};
if (stopRequested)
return;
for (const InputVector* input : inputDataShuffled)
{
if (requestStopCallback)
stopRequested = requestStopCallback();
if (stopRequested)
return;
updateRefVectors(getClosestRefVectorPosition(*input), *input, learningFactor, curIter);
}
if (stopRequested)
return;
}
}
const InputVector&
Network::getRefVector(const Position& position) const
{
return _refVectors[position];
}
updateRefVectors(getClosestRefVectorPosition(*input), *input, learningFactor, curIter);
}
if (stopRequested)
return;
}
}
const InputVector& Network::getRefVector(const Position& position) const
{
return _refVectors[position];
}
} // namespace lms::som
+21 -23
View File
@@ -19,42 +19,40 @@
#pragma once
#include <vector>
#include <ostream>
#include <vector>
#include "Network.hpp"
namespace lms::som
{
class DataNormalizer
{
public:
struct MinMax
{
InputVector::value_type min;
InputVector::value_type max;
};
class DataNormalizer
{
public:
struct MinMax
{
InputVector::value_type min;
InputVector::value_type max;
};
DataNormalizer(std::size_t inputDimCount);
DataNormalizer(std::size_t inputDimCount);
std::size_t getInputDimCount() const { return _inputDimCount; }
const MinMax& getValue(std::size_t index) const;
std::size_t getInputDimCount() const { return _inputDimCount; }
const MinMax& getValue(std::size_t index) const;
void setValue(std::size_t index, const MinMax& minMax);
void setValue(std::size_t index, const MinMax& minMax);
void computeNormalizationFactors(const std::vector<InputVector>& dataSamples);
void computeNormalizationFactors(const std::vector<InputVector>& dataSamples);
void normalizeData(InputVector& data) const;
void normalizeData(InputVector& data) const;
void dump(std::ostream& os) const;
void dump(std::ostream& os) const;
private:
InputVector::value_type normalizeValue(InputVector::value_type value, std::size_t dimensionId) const;
private:
InputVector::value_type normalizeValue(InputVector::value_type value, std::size_t dimensionId) const;
const std::size_t _inputDimCount;
std::vector<MinMax> _minmax; // Indexed min/max used to normalize data
};
const std::size_t _inputDimCount;
std::vector<MinMax> _minmax; // Indexed min/max used to normalize data
};
} // namespace lms::som
+131 -133
View File
@@ -20,176 +20,174 @@
#pragma once
#include <vector>
#include <cmath>
#include <vector>
#include "core/Exception.hpp"
namespace lms::som
{
class Exception : public core::LmsException
{
public:
using LmsException::LmsException;
};
class Exception : public core::LmsException
{
public:
using LmsException::LmsException;
};
class InputVector
{
public:
using value_type = double;
using Norm = double;
using Distance = double;
class InputVector
{
public:
using value_type = double;
using Norm = double;
using Distance = double;
InputVector(std::size_t nbDimensions, value_type defaultValue = value_type{})
: _values(nbDimensions, defaultValue) {}
InputVector(std::size_t nbDimensions, value_type defaultValue = value_type {}) : _values(nbDimensions, defaultValue) {}
bool hasSameDimension(const InputVector& other) const
{
return _values.size() == other._values.size();
}
bool hasSameDimension(const InputVector& other) const
{
return _values.size() == other._values.size();
}
std::size_t getNbDimensions() const
{
return _values.size();
}
std::size_t getNbDimensions() const
{
return _values.size();
}
value_type& operator[](std::size_t index)
{
if (index >= getNbDimensions())
throw Exception("Bad range");
value_type& operator[](std::size_t index)
{
if (index >= getNbDimensions())
throw Exception("Bad range");
return _values[index];
}
return _values[index];
}
value_type operator[](std::size_t index) const
{
if (index >= getNbDimensions())
throw Exception("Bad range");
value_type operator[](std::size_t index) const
{
if (index >= getNbDimensions())
throw Exception("Bad range");
return _values[index];
}
return _values[index];
}
InputVector& operator+=(const InputVector& other)
{
if (!hasSameDimension(other.getNbDimensions()))
throw Exception{ "Not the same dimension count" };
InputVector& operator+=(const InputVector& other)
{
if (!hasSameDimension(other.getNbDimensions()))
throw Exception {"Not the same dimension count"};
for (std::size_t i{}; i < _values.size(); ++i)
{
_values[i] += other[i];
}
for (std::size_t i {}; i < _values.size(); ++i)
{
_values[i] += other[i];
}
return *this;
}
return *this;
}
InputVector& operator-=(const InputVector& other)
{
if (!hasSameDimension(other.getNbDimensions()))
throw Exception{ "Not the same dimension count" };
InputVector& operator-=(const InputVector& other)
{
if (!hasSameDimension(other.getNbDimensions()))
throw Exception {"Not the same dimension count"};
for (std::size_t i{}; i < _values.size(); ++i)
{
_values[i] -= other[i];
}
for (std::size_t i {}; i < _values.size(); ++i)
{
_values[i] -= other[i];
}
return *this;
}
return *this;
}
InputVector& operator*=(value_type factor)
{
for (std::size_t i{}; i < _values.size(); ++i)
{
_values[i] *= factor;
}
InputVector& operator*=(value_type factor)
{
for (std::size_t i {}; i < _values.size(); ++i)
{
_values[i] *= factor;
}
return *this;
}
return *this;
}
Norm computeNorm() const
{
Norm res{};
for (value_type val : _values)
res += val * val;
return std::sqrt(res);
}
Norm computeNorm() const
{
Norm res {};
for (value_type val : _values)
res += val * val;
return std::sqrt(res);
}
Distance computeEuclidianSquareDistance(const InputVector& other, const InputVector& weights) const
{
if (!hasSameDimension(other.getNbDimensions())
|| !hasSameDimension(weights.getNbDimensions()))
{
throw Exception{ "Not the same dimension count" };
}
Distance computeEuclidianSquareDistance(const InputVector& other, const InputVector& weights) const
{
if (!hasSameDimension(other.getNbDimensions())
|| !hasSameDimension(weights.getNbDimensions()))
{
throw Exception {"Not the same dimension count"};
}
Distance res{};
Distance res {};
for (std::size_t i{}; i < getNbDimensions(); ++i)
{
const InputVector::value_type diff{ _values[i] - other._values[i] };
res += diff * diff * weights._values[i];
}
for (std::size_t i {}; i < getNbDimensions(); ++i)
{
const InputVector::value_type diff {_values[i] - other._values[i]};
res += diff * diff * weights._values[i];
}
return res;
}
return res;
}
std::vector<value_type>::iterator begin()
{
return _values.begin();
}
std::vector<value_type>::iterator begin()
{
return _values.begin();
}
std::vector<value_type>::const_iterator begin() const
{
return _values.cbegin();
}
std::vector<value_type>::const_iterator begin() const
{
return _values.cbegin();
}
std::vector<value_type>::const_iterator cbegin() const
{
return _values.cbegin();
}
std::vector<value_type>::const_iterator cbegin() const
{
return _values.cbegin();
}
std::vector<value_type>::iterator end()
{
return _values.end();
}
std::vector<value_type>::iterator end()
{
return _values.end();
}
std::vector<value_type>::const_iterator end() const
{
return _values.cend();
}
std::vector<value_type>::const_iterator end() const
{
return _values.cend();
}
std::vector<value_type>::const_iterator cend() const
{
return _values.cend();
}
std::vector<value_type>::const_iterator cend() const
{
return _values.cend();
}
private:
friend class InputVector operator-(const InputVector& a, const InputVector& b)
{
if (!a.hasSameDimension(b.getNbDimensions()))
throw Exception{ "Not the same dimension count" };
private:
friend class InputVector operator-(const InputVector& a, const InputVector& b)
{
if (!a.hasSameDimension(b.getNbDimensions()))
throw Exception {"Not the same dimension count"};
InputVector res{ a.getNbDimensions() };
InputVector res {a.getNbDimensions()};
for (std::size_t i{}; i < res._values.size(); ++i)
res._values[i] = a._values[i] - b._values[i];
for (std::size_t i {}; i < res._values.size(); ++i)
res._values[i] = a._values[i] - b._values[i];
return res;
}
return res;
}
friend std::ostream& operator<<(std::ostream& os, const InputVector& a)
{
os << "[";
for (value_type val : a._values)
{
os << val << " ";
}
os << "]";
friend std::ostream&
operator<<(std::ostream& os, const InputVector& a)
{
os << "[";
for (value_type val : a._values)
{
os << val << " ";
}
os << "]";
return os;
}
return os;
}
std::vector<value_type> _values;
};
}
std::vector<value_type> _values;
};
} // namespace lms::som
+8 -9
View File
@@ -47,7 +47,7 @@ namespace lms::som
}
};
template <typename T>
template<typename T>
class Matrix
{
public:
@@ -61,7 +61,7 @@ namespace lms::som
}
template<typename... CtrArgs>
Matrix(Coordinate width, Coordinate height, CtrArgs&& ... args)
Matrix(Coordinate width, Coordinate height, CtrArgs&&... args)
: _width{ width }
, _height{ height }
{
@@ -93,7 +93,7 @@ namespace lms::som
T& operator[](const Position& position) { return get(position); }
const T& operator[](const Position& position) const { return get(position); }
template <typename Func>
template<typename Func>
Position getPositionMinElement(Func func) const
{
assert(!_values.empty());
@@ -105,12 +105,12 @@ namespace lms::som
}
private:
Coordinate _width{};
Coordinate _height{};
std::vector<T> _values;
Coordinate _width{};
Coordinate _height{};
std::vector<T> _values;
};
} // ns lms::som
} // namespace lms::som
namespace std
{
@@ -125,5 +125,4 @@ namespace std
return h1 ^ (h2 << 1);
}
};
} // ns std
} // namespace std
+3 -6
View File
@@ -19,17 +19,16 @@
#pragma once
#include <vector>
#include <functional>
#include <optional>
#include <ostream>
#include <functional>
#include <vector>
#include "InputVector.hpp"
#include "Matrix.hpp"
namespace lms::som
{
using LearningFactor = InputVector::value_type;
using Norm = InputVector::value_type;
@@ -92,16 +91,14 @@ namespace lms::som
void setNeighbourhoodFunc(NeighbourhoodFunc neighbourhoodFunc);
private:
void updateRefVectors(const Position& closestRefVectorPosition, const InputVector& input, LearningFactor learningFactor, const CurrentIteration& iteration);
std::size_t _inputDimCount{};
InputVector _weights; // weight for each dimension
InputVector _weights; // weight for each dimension
Matrix<InputVector> _refVectors;
DistanceFunc _distanceFunc;
LearningFactorFunc _learningFactorFunc;
NeighbourhoodFunc _neighbourhoodFunc;
};
} // namespace lms::som
+89 -88
View File
@@ -18,117 +18,118 @@
*/
#include <unordered_set>
#include <gtest/gtest.h>
#include "som/DataNormalizer.hpp"
#include "som/Network.hpp"
namespace lms::som
{
static constexpr InputVector::value_type EPSILON = 0.01;
static constexpr InputVector::value_type EPSILON = 0.01;
TEST(som, Matrix)
{
{
Matrix<int> testMatrix{ 2, 2, 123 };
{
const Position pos{ 0, 0 };
EXPECT_EQ(testMatrix[pos], 123);
}
{
const Position pos{ 0, 1 };
EXPECT_EQ(testMatrix[pos], 123);
}
{
const Position pos{ 1, 0 };
EXPECT_EQ(testMatrix[pos], 123);
}
{
const Position pos{ 1, 1 };
EXPECT_EQ(testMatrix[pos], 123);
}
}
}
TEST(som, Matrix)
{
{
Matrix<int> testMatrix{ 2, 2, 123 };
{
const Position pos{ 0, 0 };
EXPECT_EQ(testMatrix[pos], 123);
}
{
const Position pos{ 0, 1 };
EXPECT_EQ(testMatrix[pos], 123);
}
{
const Position pos{ 1, 0 };
EXPECT_EQ(testMatrix[pos], 123);
}
{
const Position pos{ 1, 1 };
EXPECT_EQ(testMatrix[pos], 123);
}
}
}
TEST(som, InputVector)
{
{
InputVector test1{ 2 };
test1[0] = 0;
test1[1] = 1;
TEST(som, InputVector)
{
{
InputVector test1{ 2 };
test1[0] = 0;
test1[1] = 1;
InputVector test2{ 2 };
test2[0] = 1;
test2[1] = 0;
InputVector test2{ 2 };
test2[0] = 1;
test2[1] = 0;
InputVector test3{ test1 };
test3 += test2;
EXPECT_LT(std::abs(test3[0] - 1), EPSILON);
EXPECT_LT(std::abs(test3[1] - 1), EPSILON);
}
}
InputVector test3{ test1 };
test3 += test2;
EXPECT_LT(std::abs(test3[0] - 1), EPSILON);
EXPECT_LT(std::abs(test3[1] - 1), EPSILON);
}
}
TEST(som, Network)
{
Network network{ 2, 2, 1 };
TEST(som, Network)
{
Network network{ 2, 2, 1 };
const InputVector weights{ 1, 1 };
std::vector<InputVector> trainData
{
{ 1, 50 },
{ 1, 100 },
{ 1, 150 },
{ 1, 200 },
};
const InputVector weights{ 1, 1 };
std::vector<InputVector> trainData{
{ 1, 50 },
{ 1, 100 },
{ 1, 150 },
{ 1, 200 },
};
DataNormalizer normalizer{ 1 };
normalizer.computeNormalizationFactors(trainData);
for (auto& data : trainData)
normalizer.normalizeData(data);
DataNormalizer normalizer{ 1 };
normalizer.computeNormalizationFactors(trainData);
for (auto& data : trainData)
normalizer.normalizeData(data);
network.dump(std::cout);
network.train(trainData, 20);
network.dump(std::cout);
network.dump(std::cout);
network.train(trainData, 20);
network.dump(std::cout);
auto distFunc{ network.getDistanceFunc() };
auto distFunc{ network.getDistanceFunc() };
EXPECT_LT((std::abs(distFunc({ 1, 0 }, { 1, 1 }, weights) - 1)), EPSILON);
EXPECT_LT((std::abs(distFunc({ 1, 0 }, { 1, 2 }, weights) - 4)), EPSILON);
EXPECT_LT(std::abs(distFunc({ 1, 0 }, { 1, 0.33 }, weights) - distFunc({ 1, 0.66 }, { 1, 1. }, weights)), EPSILON);
EXPECT_LT((std::abs(distFunc({ 1, 0 }, { 1, 1 }, weights) - 1)), EPSILON);
EXPECT_LT((std::abs(distFunc({ 1, 0 }, { 1, 2 }, weights) - 4)), EPSILON);
EXPECT_LT(std::abs(distFunc({ 1, 0 }, { 1, 0.33 }, weights) - distFunc({ 1, 0.66 }, { 1, 1. }, weights)), EPSILON);
{
std::unordered_set<Position> positions;
for (const InputVector& data : trainData)
positions.insert(network.getClosestRefVectorPosition(data));
{
std::unordered_set<Position> positions;
for (const InputVector& data : trainData)
positions.insert(network.getClosestRefVectorPosition(data));
EXPECT_EQ(positions.size(), 4);
}
EXPECT_EQ(positions.size(), 4);
}
{
Position pos{ network.getClosestRefVectorPosition(InputVector{1, 0.66}) };
for (std::size_t i{}; i < 40; ++i)
{
InputVector input{ 1, 130 + static_cast<InputVector::value_type>(i) };
normalizer.normalizeData(input);
{
Position pos{ network.getClosestRefVectorPosition(InputVector{ 1, 0.66 }) };
for (std::size_t i{}; i < 40; ++i)
{
InputVector input{ 1, 130 + static_cast<InputVector::value_type>(i) };
normalizer.normalizeData(input);
EXPECT_EQ(network.getClosestRefVectorPosition(input), pos);
}
}
EXPECT_EQ(network.getClosestRefVectorPosition(input), pos);
}
}
{
Position pos{ network.getClosestRefVectorPosition(InputVector{1, 1}) };
for (std::size_t i{}; i < 40; ++i)
{
InputVector input{ 1, 180 + static_cast<InputVector::value_type>(i) };
normalizer.normalizeData(input);
{
Position pos{ network.getClosestRefVectorPosition(InputVector{ 1, 1 }) };
for (std::size_t i{}; i < 40; ++i)
{
InputVector input{ 1, 180 + static_cast<InputVector::value_type>(i) };
normalizer.normalizeData(input);
EXPECT_EQ(network.getClosestRefVectorPosition(input), pos);
}
}
}
}
EXPECT_EQ(network.getClosestRefVectorPosition(input), pos);
}
}
}
} // namespace lms::som
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}