From 289a8da39e387e653046c926981a4ac619002ef4 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 22 Dec 2017 15:33:23 +0300 Subject: [PATCH 1/2] ml: simplify interfaces of SimulatedAnnealingSolver --- modules/ml/include/opencv2/ml.hpp | 78 ++++-------- modules/ml/include/opencv2/ml/ml.inl.hpp | 60 ++++++++++ modules/ml/src/ann_mlp.cpp | 144 +---------------------- samples/cpp/travelsalesman.cpp | 25 ++-- 4 files changed, 99 insertions(+), 208 deletions(-) create mode 100644 modules/ml/include/opencv2/ml/ml.inl.hpp diff --git a/modules/ml/include/opencv2/ml.hpp b/modules/ml/include/opencv2/ml.hpp index c276bcf72c..0295a07650 100644 --- a/modules/ml/include/opencv2/ml.hpp +++ b/modules/ml/include/opencv2/ml.hpp @@ -1912,75 +1912,49 @@ public: * Simulated annealing solver * \****************************************************************************************/ -/** @brief The class defines interface for system state used in simulated annealing optimization algorithm. +#ifdef CV_DOXYGEN +/** @brief This class declares example interface for system state used in simulated annealing optimization algorithm. -@cite Kirkpatrick83 for details +@note This class is not defined in C++ code and can't be use directly - you need your own implementation with the same methods. */ -class CV_EXPORTS SimulatedAnnealingSolverSystem +struct SimulatedAnnealingSolverSystem { -protected: - inline SimulatedAnnealingSolverSystem() {} -public: - virtual ~SimulatedAnnealingSolverSystem() {} - /** Give energy value for a state of system.*/ - virtual double energy() const = 0; + double energy() const; /** Function which change the state of system (random pertubation).*/ - virtual void changeState() = 0; + void changeState(); /** Function to reverse to the previous state. Can be called once only after changeState(). */ - virtual void reverseState() = 0; + void reverseState(); }; +#endif // CV_DOXYGEN /** @brief The class implements simulated annealing for optimization. - * + @cite Kirkpatrick83 for details + +@param solverSystem optimization system (see SimulatedAnnealingSolverSystem) +@param initialTemperature initial temperature +@param finalTemperature final temperature +@param coolingRatio temperature step multiplies +@param iterationsPerStep number of iterations per temperature changing step +@param lastTemperature optional output for last used temperature +@param rngEnergy specify custom random numbers generator (cv::theRNG() by default) */ -class CV_EXPORTS SimulatedAnnealingSolver : public Algorithm -{ -public: - SimulatedAnnealingSolver(const Ptr& system); - inline ~SimulatedAnnealingSolver() { release(); } - - /** Simulated annealing procedure. */ - int run(); - /** Set/initialize RNG (energy). - @param rng new RNG - */ - void setEnergyRNG(const RNG& rng); - /** Set initial temperature of simulated annealing procedure. - @param x new initial temperature. x\>0 - */ - void setInitialTemperature(double x); - /** Set final temperature of simulated annealing procedure. - @param x new final temperature value. 0\ 0 - */ - void setIterPerStep(int ite); - - void release(); - SimulatedAnnealingSolver(const SimulatedAnnealingSolver&); - SimulatedAnnealingSolver& operator=(const SimulatedAnnealingSolver&); - - struct Impl; friend struct Impl; -protected: - Impl* impl; -}; - +template +int simulatedAnnealingSolver(SimulatedAnnealingSolverSystem& solverSystem, + double initialTemperature, double finalTemperature, double coolingRatio, + size_t iterationsPerStep, + CV_OUT double* lastTemperature = NULL, + cv::RNG& rngEnergy = cv::theRNG() +); //! @} ml } } +#include + #endif // __cplusplus #endif // OPENCV_ML_HPP diff --git a/modules/ml/include/opencv2/ml/ml.inl.hpp b/modules/ml/include/opencv2/ml/ml.inl.hpp new file mode 100644 index 0000000000..dc9c78393a --- /dev/null +++ b/modules/ml/include/opencv2/ml/ml.inl.hpp @@ -0,0 +1,60 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#ifndef OPENCV_ML_INL_HPP +#define OPENCV_ML_INL_HPP + +namespace cv { namespace ml { + +// declared in ml.hpp +template +int simulatedAnnealingSolver(SimulatedAnnealingSolverSystem& solverSystem, + double initialTemperature, double finalTemperature, double coolingRatio, + size_t iterationsPerStep, + CV_OUT double* lastTemperature, + cv::RNG& rngEnergy +) +{ + CV_Assert(finalTemperature > 0); + CV_Assert(initialTemperature > finalTemperature); + CV_Assert(iterationsPerStep > 0); + CV_Assert(coolingRatio < 1.0f); + double Ti = initialTemperature; + double previousEnergy = solverSystem.energy(); + int exchange = 0; + while (Ti > finalTemperature) + { + for (size_t i = 0; i < iterationsPerStep; i++) + { + solverSystem.changeState(); + double newEnergy = solverSystem.energy(); + if (newEnergy < previousEnergy) + { + previousEnergy = newEnergy; + exchange++; + } + else + { + double r = rngEnergy.uniform(0.0, 1.0); + if (r < std::exp(-(newEnergy - previousEnergy) / Ti)) + { + previousEnergy = newEnergy; + exchange++; + } + else + { + solverSystem.reverseState(); + } + } + } + Ti *= coolingRatio; + } + if (lastTemperature) + *lastTemperature = Ti; + return exchange; +} + +}} //namespace + +#endif // OPENCV_ML_INL_HPP diff --git a/modules/ml/src/ann_mlp.cpp b/modules/ml/src/ann_mlp.cpp index cd10c0d218..8eae834413 100644 --- a/modules/ml/src/ann_mlp.cpp +++ b/modules/ml/src/ann_mlp.cpp @@ -42,41 +42,6 @@ namespace cv { namespace ml { -struct SimulatedAnnealingSolver::Impl -{ - int refcount; - - const Ptr systemPtr; - SimulatedAnnealingSolverSystem& system; - RNG rEnergy; - double coolingRatio; - double initialT; - double finalT; - int iterPerStep; - - Impl(const Ptr& s) : - refcount(1), - systemPtr(s), - system(*(s.get())), - rEnergy(12345) - { - CV_Assert(!systemPtr.empty()); - initialT = 2; - finalT = 0.1; - coolingRatio = 0.95; - iterPerStep = 100; - } - - inline double energy() { return system.energy(); } - inline void changeState() { system.changeState(); } - inline void reverseState() { system.reverseState(); } - - void addref() { CV_XADD(&refcount, 1); } - void release() { if (CV_XADD(&refcount, -1) == 1) delete this; } -protected: - virtual ~Impl() { CV_Assert(refcount==0); } -}; - struct AnnParams { AnnParams() @@ -115,103 +80,7 @@ inline T inBounds(T val, T min_val, T max_val) return std::min(std::max(val, min_val), max_val); } -SimulatedAnnealingSolver::SimulatedAnnealingSolver(const Ptr& system) -{ - impl = new Impl(system); -} - -SimulatedAnnealingSolver::SimulatedAnnealingSolver(const SimulatedAnnealingSolver& b) -{ - if (b.impl) b.impl->addref(); - release(); - impl = b.impl; -} - -void SimulatedAnnealingSolver::release() -{ - if (impl) { impl->release(); impl = NULL; } -} - -void SimulatedAnnealingSolver::setIterPerStep(int ite) -{ - CV_Assert(impl); - CV_Assert(ite>0); - impl->iterPerStep = ite; -} - -int SimulatedAnnealingSolver::run() -{ - CV_Assert(impl); - CV_Assert(impl->initialT>impl->finalT); - double Ti = impl->initialT; - double previousEnergy = impl->energy(); - int exchange = 0; - while (Ti > impl->finalT) - { - for (int i = 0; i < impl->iterPerStep; i++) - { - impl->changeState(); - double newEnergy = impl->energy(); - if (newEnergy < previousEnergy) - { - previousEnergy = newEnergy; - exchange++; - } - else - { - double r = impl->rEnergy.uniform(0.0, 1.0); - if (r < std::exp(-(newEnergy - previousEnergy) / Ti)) - { - previousEnergy = newEnergy; - exchange++; - } - else - { - impl->reverseState(); - } - } - - } - Ti *= impl->coolingRatio; - } - impl->finalT = Ti; - return exchange; -} - -void SimulatedAnnealingSolver::setEnergyRNG(const RNG& rng) -{ - CV_Assert(impl); - impl->rEnergy = rng; -} - -void SimulatedAnnealingSolver::setInitialTemperature(double x) -{ - CV_Assert(impl); - CV_Assert(x>0); - impl->initialT = x; -} - -void SimulatedAnnealingSolver::setFinalTemperature(double x) -{ - CV_Assert(impl); - CV_Assert(x>0); - impl->finalT = x; -} - -double SimulatedAnnealingSolver::getFinalTemperature() -{ - CV_Assert(impl); - return impl->finalT; -} - -void SimulatedAnnealingSolver::setCoolingRatio(double x) -{ - CV_Assert(impl); - CV_Assert(x>0 && x<1); - impl->coolingRatio = x; -} - -class SimulatedAnnealingANN_MLP : public SimulatedAnnealingSolverSystem +class SimulatedAnnealingANN_MLP { protected: ml::ANN_MLP& nn; @@ -228,7 +97,7 @@ public: initVarMap(); } ~SimulatedAnnealingANN_MLP() {} -protected: + void changeState() { index = rIndex.uniform(0, nbVariables); @@ -1075,14 +944,9 @@ public: } int train_anneal(const Ptr& trainData) { - SimulatedAnnealingSolver t(Ptr(new SimulatedAnnealingANN_MLP(*this, trainData))); - t.setEnergyRNG(params.rEnergy); - t.setFinalTemperature(params.finalT); - t.setInitialTemperature(params.initialT); - t.setCoolingRatio(params.coolingRatio); - t.setIterPerStep(params.itePerStep); + SimulatedAnnealingANN_MLP s(*this, trainData); trained = true; // Enable call to CalcError - int iter = t.run(); + int iter = simulatedAnnealingSolver(s, params.initialT, params.finalT, params.coolingRatio, params.itePerStep, NULL, params.rEnergy); trained =false; return iter; } diff --git a/samples/cpp/travelsalesman.cpp b/samples/cpp/travelsalesman.cpp index 5a355b04cd..37eea67874 100644 --- a/samples/cpp/travelsalesman.cpp +++ b/samples/cpp/travelsalesman.cpp @@ -2,7 +2,7 @@ using namespace cv; -class TravelSalesman : public ml::SimulatedAnnealingSolverSystem +class TravelSalesman { private : const std::vector& posCity; @@ -17,11 +17,11 @@ public: rng = theRNG(); } /** Give energy value for a state of system.*/ - /*virtual*/ double energy() const; + double energy() const; /** Function which change the state of system (random pertubation).*/ - /*virtual*/ void changeState(); + void changeState(); /** Function to reverse to the previous state.*/ - /*virtual*/ void reverseState(); + void reverseState(); }; @@ -81,31 +81,24 @@ int main(void) posCity[i].y = static_cast(radius*sin(theta)) + center.y; next[i]=(i+1)%nbCity; } - Ptr ts_system(new TravelSalesman(posCity, next)); - ml::SimulatedAnnealingSolver ts(ts_system); + TravelSalesman ts_system(posCity, next); - ts.setIterPerStep(10000*nbCity); - ts.setCoolingRatio(0.99); - ts.setInitialTemperature(100); - ts.setFinalTemperature(100*0.97); DrawTravelMap(img,posCity,next); imshow("Map",img); waitKey(10); + double currentTemperature = 100.0; for (int i = 0, zeroChanges = 0; zeroChanges < 10; i++) { - int changesApplied = ts.run(); - img = Mat::zeros(img.size(),CV_8UC3); + int changesApplied = ml::simulatedAnnealingSolver(ts_system, currentTemperature, currentTemperature*0.97, 0.99, 10000*nbCity, ¤tTemperature, rng); + img.setTo(Scalar::all(0)); DrawTravelMap(img, posCity, next); imshow("Map", img); int k = waitKey(10); - double ti=ts.getFinalTemperature(); - std::cout << "i=" << i << " changesApplied=" << changesApplied << " temp=" << ti << " result=" << ts_system->energy() << std::endl; + std::cout << "i=" << i << " changesApplied=" << changesApplied << " temp=" << currentTemperature << " result=" << ts_system.energy() << std::endl; if (k == 27 || k == 'q' || k == 'Q') return 0; if (changesApplied == 0) zeroChanges++; - ts.setInitialTemperature(ti); - ts.setFinalTemperature(ti*0.97); } std::cout << "Done" << std::endl; waitKey(0); From 00e43a9022e2c8e6d4ec2c7dd8414273a278ca07 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 22 Dec 2017 18:50:04 +0000 Subject: [PATCH 2/2] ml(ANN_MLP): ensure that train() call is always successful --- modules/ml/src/ann_mlp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ml/src/ann_mlp.cpp b/modules/ml/src/ann_mlp.cpp index 8eae834413..cd00024121 100644 --- a/modules/ml/src/ann_mlp.cpp +++ b/modules/ml/src/ann_mlp.cpp @@ -948,7 +948,7 @@ public: trained = true; // Enable call to CalcError int iter = simulatedAnnealingSolver(s, params.initialT, params.finalT, params.coolingRatio, params.itePerStep, NULL, params.rEnergy); trained =false; - return iter; + return iter + 1; // ensure that 'train()' call is always successful } int train_backprop( const Mat& inputs, const Mat& outputs, const Mat& _sw, TermCriteria termCrit )