Auto reformatted the base, ref #470
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user