diff --git a/src/classifier/Clusterer.hpp b/src/classifier/Clusterer.hpp
new file mode 100644
index 00000000..7b9d31e5
--- /dev/null
+++ b/src/classifier/Clusterer.hpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 Emeric Poupon
+ *
+ * This file is part of LMS.
+ *
+ * LMS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LMS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LMS. If not, see .
+ */
+
+#pragma once
+
+
+#include "SOM.hpp"
+#include "DataNormalizer.hpp"
+
+/*
+ * For each InputVector, associate vector values
+ */
+template
+class Clusterer
+{
+ public:
+ using SampleType = std::pair;
+ Clusterer(const std::vector& samples, std::size_t inputDimCount, std::size_t iterationCount);
+
+ const std::vector& getClusterValues(const SOM::InputVector& data) const;
+
+ void dump(std::ostream& os) const;
+
+ private:
+
+ void train(const std::vector>& samples, std::size_t iterationCount);
+
+ std::vector& getValues(SOM::Coords coords);
+ const std::vector& getValues(SOM::Coords coords) const;
+
+ std::size_t _width;
+ std::size_t _height;
+ std::vector> _values; // Map of T vectors
+ SOM::DataNormalizer _dataNormalizer;
+ SOM::Network _network;
+};
+
+
+template
+Clusterer::Clusterer(const std::vector& samples, std::size_t inputDimCount, std::size_t iterationCount)
+:
+_width(3),
+_height(3),
+_dataNormalizer(inputDimCount),
+_network(_width, _height, inputDimCount)
+{
+ _values.resize(_width * _height);
+ train(samples, iterationCount);
+}
+
+template
+std::vector&
+Clusterer::getValues(SOM::Coords coords)
+{
+ return _values[ coords.x + coords.y*_width ];
+}
+
+template
+const std::vector&
+Clusterer::getValues(SOM::Coords coords) const
+{
+ return _values[ coords.x + coords.y*_width ];
+}
+
+
+template
+void
+Clusterer::train(const std::vector>& samples, std::size_t iterationCount)
+{
+ // Train
+ {
+ std::vector inputVectors;
+ inputVectors.reserve(samples.size());
+
+ for (const auto& sample : samples)
+ {
+ inputVectors.push_back(sample.first);
+ }
+
+ _dataNormalizer.computeNormalizationFactors(inputVectors);
+
+ for (auto& inputVector : inputVectors)
+ _dataNormalizer.normalizeData(inputVector);
+
+ _network.train(inputVectors, iterationCount);
+ }
+
+ // Classify data
+ for (const auto& sample : samples)
+ {
+ auto inputVector = sample.first;
+ const auto& value = sample.second;
+
+ _dataNormalizer.normalizeData(inputVector);
+ auto coords = _network.classify(inputVector);
+ auto& values = getValues(coords);
+
+ values.push_back(value);
+ }
+}
+
+template
+const std::vector&
+Clusterer::getClusterValues(const SOM::InputVector& inputVector) const
+{
+ auto inputVectorNormalized = inputVector;
+ _dataNormalizer.normalizeData(inputVectorNormalized);
+
+ return getValues(_network.classify(inputVectorNormalized));
+}
+
+template
+void
+Clusterer::dump(std::ostream& os) const
+{
+ os << "Internal network:" << std::endl;
+ _network.dump(os);
+ os << "Values: " << std::endl;
+ for (std::size_t x = 0; x < _width; ++x)
+ {
+ for (std::size_t y = 0; y < _height; ++y)
+ {
+ os << "[";
+ for (const auto& value : getValues({x, y}))
+ os << value << " ";
+ os << "] ";
+ }
+ os << std::endl;
+ }
+
+}
+
+
diff --git a/tools/classifier/LmsClassifier.cpp b/tools/classifier/LmsClassifier.cpp
index 620cd956..1e619ce0 100644
--- a/tools/classifier/LmsClassifier.cpp
+++ b/tools/classifier/LmsClassifier.cpp
@@ -6,9 +6,11 @@
#include "classifier/SOM.hpp"
#include "classifier/DataNormalizer.hpp"
+#include "classifier/Clusterer.hpp"
int main(int argc, char *argv[])
{
+
if (argc != 2)
{
std::cerr << "Usage: " << std::endl;
@@ -17,64 +19,25 @@ int main(int argc, char *argv[])
auto iterationCount = std::stoul(argv[1]);
- using namespace SOM;
-
- SOM::Network network(5, 5, 2);
-
- network.dump(std::cout);
-
- std::vector< std::vector > inputValues =
+ std::vector< std::pair, std::string> > inputValues =
{
- { 160, 1 },
- { 80, -1 },
- { 80, -0.75 },
- { 240, 0.5 },
- { 240, -0.5 },
- { 120, -0.5 },
- { 140, -0.5 },
+ {{ 160, 1 }, { "banane" }},
+ {{ 80, -1 }, { "poire" }},
+ {{ 80, -0.75 }, {"pocolat"}},
+ {{ 240, 0.5 }, {"abricot"}},
+ {{ 240, -0.5 }, {"peche"}},
+ {{ 120, -0.5 }, {"fraise"}},
+ {{ 140, -0.5 }, {"myrtille"}},
};
- std::cout << "Before normalization:" << std::endl;
- for (const auto& inputValue : inputValues)
- {
- std::cout << inputValue << std::endl;
- }
- std::cout << std::endl;
+ Clusterer classifier(inputValues, 2, iterationCount);
- std::cout << "After normalization:" << std::endl;
+ std::cout << "Clusterer :" << std::endl;
+ classifier.dump(std::cout);
- SOM::DataNormalizer normalizer(2);
-
- normalizer.computeNormalizationFactors(inputValues);
-
- for (auto& inputValue : inputValues)
- normalizer.normalizeData(inputValue);
-
- for (const auto& inputValue : inputValues)
- {
- std::cout << inputValue << std::endl;
- }
-
- std::cout << std::endl;
-
- for (const auto& inputValue : inputValues)
- {
- auto res = network.classify(inputValue);
- std::cout << "Found at " << res.x << ", " << res.y << std::endl;
- }
-
- std::cout << "Training network for " << iterationCount << " iterations" << std::endl;
-
- network.train(inputValues, iterationCount);
-
- network.dump(std::cout);
- std::cout << "OK" << std::endl;
-
- for (const auto& inputValue : inputValues)
- {
- auto res = network.classify(inputValue);
- std::cout << "Found at " << res.x << ", " << res.y << std::endl;
- }
+ std::cout << "Classify 195, 0.35 = " << std::endl;
+ for (const auto& val : classifier.getClusterValues({195, 0.35}))
+ std::cout << val << " " << std::endl;
return EXIT_SUCCESS;
}