From de712936eb080b48c019b8b5c50d65bc6a91fbb6 Mon Sep 17 00:00:00 2001 From: emeric Date: Mon, 10 Dec 2018 13:02:44 +0100 Subject: [PATCH] Adding a clusterer on top of the network --- src/classifier/Clusterer.hpp | 149 +++++++++++++++++++++++++++++ tools/classifier/LmsClassifier.cpp | 69 ++++--------- 2 files changed, 165 insertions(+), 53 deletions(-) create mode 100644 src/classifier/Clusterer.hpp 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; }