From aef3019152f33287f4c5711dd6bcc2b90ece130d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 15 Dec 2017 17:40:08 +0300 Subject: [PATCH] ml: fix SimulatedAnnealingSolver interface --- modules/ml/include/opencv2/ml.hpp | 89 ++++++++++++-------- modules/ml/src/ann_mlp.cpp | 169 ++++++++++++++++++-------------------- modules/ml/test/test_mltests2.cpp | 7 +- samples/cpp/travelsalesman.cpp | 68 +++++++-------- 4 files changed, 174 insertions(+), 159 deletions(-) diff --git a/modules/ml/include/opencv2/ml.hpp b/modules/ml/include/opencv2/ml.hpp index 1ea79f2..c276bcf 100644 --- a/modules/ml/include/opencv2/ml.hpp +++ b/modules/ml/include/opencv2/ml.hpp @@ -1528,6 +1528,9 @@ public: /** @copybrief getAnnealItePerStep @see getAnnealItePerStep */ CV_WRAP void setAnnealItePerStep(int val); + /** @brief Set/initialize anneal RNG */ + void setAnnealEnergyRNG(const RNG& rng); + /** possible activation functions */ enum ActivationFunctions { /** Identity function: \f$f(x)=x\f$ */ @@ -1875,86 +1878,104 @@ class CV_EXPORTS_W ANN_MLP_ANNEAL : public ANN_MLP { public: /** @see setAnnealInitialT */ - CV_WRAP virtual double getAnnealInitialT() const; + CV_WRAP virtual double getAnnealInitialT() const = 0; /** @copybrief getAnnealInitialT @see getAnnealInitialT */ - CV_WRAP virtual void setAnnealInitialT(double val); + CV_WRAP virtual void setAnnealInitialT(double val) = 0; /** ANNEAL: Update final temperature. It must be \>=0 and less than initialT. Default value is 0.1.*/ /** @see setAnnealFinalT */ - CV_WRAP virtual double getAnnealFinalT() const; + CV_WRAP virtual double getAnnealFinalT() const = 0; /** @copybrief getAnnealFinalT @see getAnnealFinalT */ - CV_WRAP virtual void setAnnealFinalT(double val); + CV_WRAP virtual void setAnnealFinalT(double val) = 0; /** ANNEAL: Update cooling ratio. It must be \>0 and less than 1. Default value is 0.95.*/ /** @see setAnnealCoolingRatio */ - CV_WRAP virtual double getAnnealCoolingRatio() const; + CV_WRAP virtual double getAnnealCoolingRatio() const = 0; /** @copybrief getAnnealCoolingRatio @see getAnnealCoolingRatio */ - CV_WRAP virtual void setAnnealCoolingRatio(double val); + CV_WRAP virtual void setAnnealCoolingRatio(double val) = 0; /** ANNEAL: Update iteration per step. It must be \>0 . Default value is 10.*/ /** @see setAnnealItePerStep */ - CV_WRAP virtual int getAnnealItePerStep() const; + CV_WRAP virtual int getAnnealItePerStep() const = 0; /** @copybrief getAnnealItePerStep @see getAnnealItePerStep */ - CV_WRAP virtual void setAnnealItePerStep(int val); - - - /** @brief Creates empty model - - Use StatModel::train to train the model, Algorithm::load\(filename) to load the pre-trained model. - Note that the train method has optional flags: ANN_MLP::TrainFlags. - */ -// CV_WRAP static Ptr create(); + CV_WRAP virtual void setAnnealItePerStep(int val) = 0; + /** @brief Set/initialize anneal RNG */ + virtual void setAnnealEnergyRNG(const RNG& rng) = 0; }; + /****************************************************************************************\ * Simulated annealing solver * \****************************************************************************************/ +/** @brief The class defines interface for system state used in simulated annealing optimization algorithm. + +@cite Kirkpatrick83 for details +*/ +class CV_EXPORTS SimulatedAnnealingSolverSystem +{ +protected: + inline SimulatedAnnealingSolverSystem() {} +public: + virtual ~SimulatedAnnealingSolverSystem() {} + + /** Give energy value for a state of system.*/ + virtual double energy() const = 0; + /** Function which change the state of system (random pertubation).*/ + virtual void changeState() = 0; + /** Function to reverse to the previous state. Can be called once only after changeState(). */ + virtual void reverseState() = 0; +}; + /** @brief The class implements simulated annealing for optimization. + * @cite Kirkpatrick83 for details */ class CV_EXPORTS SimulatedAnnealingSolver : public Algorithm { public: - SimulatedAnnealingSolver() { init(); } - ~SimulatedAnnealingSolver(); - /** Give energy value for a state of system.*/ - virtual double energy() =0; - /** Function which change the state of system (random pertubation).*/ - virtual void changedState() = 0; - /** Function to reverse to the previous state.*/ - virtual void reverseChangedState() = 0; - /** Simulated annealing procedure. */ + SimulatedAnnealingSolver(const Ptr& system); + inline ~SimulatedAnnealingSolver() { release(); } + + /** Simulated annealing procedure. */ int run(); - /** Set intial temperature of simulated annealing procedure. - *@param x new initial temperature. x\>0 + /** 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 + @param ite number of iteration per temperature step ite \> 0 */ void setIterPerStep(int ite); -protected: - void init(); + void release(); + SimulatedAnnealingSolver(const SimulatedAnnealingSolver&); + SimulatedAnnealingSolver& operator=(const SimulatedAnnealingSolver&); -private: - struct Impl; + struct Impl; friend struct Impl; +protected: Impl* impl; }; + + //! @} ml } diff --git a/modules/ml/src/ann_mlp.cpp b/modules/ml/src/ann_mlp.cpp index 2ab0537..5652da2 100644 --- a/modules/ml/src/ann_mlp.cpp +++ b/modules/ml/src/ann_mlp.cpp @@ -44,21 +44,37 @@ 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() + + 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; - refcount = 1; } - int refcount; - ~Impl() { refcount--;CV_Assert(refcount==0); } + + 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 @@ -71,7 +87,7 @@ struct AnnParams rpDW0 = 0.1; rpDWPlus = 1.2; rpDWMinus = 0.5; rpDWMin = FLT_EPSILON; rpDWMax = 50.; initialT=10;finalT=0.1,coolingRatio=0.95;itePerStep=10; - + rEnergy = cv::RNG(12345); } TermCriteria termCrit; @@ -90,6 +106,7 @@ struct AnnParams double finalT; double coolingRatio; int itePerStep; + RNG rEnergy; }; template @@ -98,48 +115,60 @@ inline T inBounds(T val, T min_val, T max_val) return std::min(std::max(val, min_val), max_val); } -SimulatedAnnealingSolver::~SimulatedAnnealingSolver() +SimulatedAnnealingSolver::SimulatedAnnealingSolver(const Ptr& system) +{ + impl = new Impl(system); +} + +SimulatedAnnealingSolver::SimulatedAnnealingSolver(const SimulatedAnnealingSolver& b) { - if (impl) delete impl; + if (b.impl) b.impl->addref(); + release(); + impl = b.impl; } -void SimulatedAnnealingSolver::init() +void SimulatedAnnealingSolver::release() { - impl = new SimulatedAnnealingSolver::Impl(); + 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 = energy(); + double previousEnergy = impl->energy(); int exchange = 0; while (Ti > impl->finalT) { for (int i = 0; i < impl->iterPerStep; i++) { - changedState(); - double newEnergy = energy(); + impl->changeState(); + double newEnergy = impl->energy(); if (newEnergy < previousEnergy) { previousEnergy = newEnergy; + //??? exchange++; } else { - double r = impl->rEnergy.uniform(double(0.0), double(1.0)); - if (r < exp(-(newEnergy - previousEnergy) / Ti)) + double r = impl->rEnergy.uniform(0.0, 1.0); + if (r < std::exp(-(newEnergy - previousEnergy) / Ti)) { previousEnergy = newEnergy; exchange++; } else - reverseChangedState(); + { + impl->reverseState(); + } } } @@ -149,33 +178,43 @@ int SimulatedAnnealingSolver::run() 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 ml::SimulatedAnnealingSolver +class SimulatedAnnealingANN_MLP : public SimulatedAnnealingSolverSystem { -public: - ml::ANN_MLP *nn; +protected: + ml::ANN_MLP& nn; Ptr data; int nbVariables; vector adrVariables; @@ -183,13 +222,14 @@ public: RNG rIndex; double varTmp; int index; - - SimulatedAnnealingANN_MLP(ml::ANN_MLP *x, Ptr d) : nn(x), data(d) +public: + SimulatedAnnealingANN_MLP(ml::ANN_MLP& x, const Ptr& d) : nn(x), data(d) { initVarMap(); } - - void changedState() + ~SimulatedAnnealingANN_MLP() {} +protected: + void changeState() { index = rIndex.uniform(0, nbVariables); double dv = rVar.uniform(-1.0, 1.0); @@ -197,22 +237,22 @@ public: *adrVariables[index] = dv; } - void reverseChangedState() + void reverseState() { *adrVariables[index] = varTmp; } - double energy() { return nn->calcError(data, false, noArray()); } + double energy() const { return nn.calcError(data, false, noArray()); } protected: void initVarMap() { - Mat l = nn->getLayerSizes(); + Mat l = nn.getLayerSizes(); nbVariables = 0; adrVariables.clear(); for (int i = 1; i < l.rows-1; i++) { - Mat w = nn->getWeights(i); + Mat w = nn.getWeights(i); for (int j = 0; j < w.rows; j++) { for (int k = 0; k < w.cols; k++, nbVariables++) @@ -296,6 +336,13 @@ void ANN_MLP::setAnnealItePerStep(int val) this_->setAnnealItePerStep(val); } +void ANN_MLP::setAnnealEnergyRNG(const RNG& rng) +{ + ANN_MLP_ANNEAL* this_ = dynamic_cast(this); + if (!this_) + CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); + this_->setAnnealEnergyRNG(rng); +} class ANN_MLPImpl : public ANN_MLP_ANNEAL { @@ -323,6 +370,9 @@ public: CV_IMPL_PROPERTY(double, AnnealCoolingRatio, params.coolingRatio) CV_IMPL_PROPERTY(int, AnnealItePerStep, params.itePerStep) + //CV_IMPL_PROPERTY(RNG, AnnealEnergyRNG, params.rEnergy) + inline void setAnnealEnergyRNG(const RNG& val) { params.rEnergy = val; } + void clear() { min_val = max_val = min_val1 = max_val1 = 0.; @@ -1040,7 +1090,8 @@ public: } int train_anneal(const Ptr& trainData) { - SimulatedAnnealingANN_MLP t(this, 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); @@ -1687,70 +1738,6 @@ Ptr ANN_MLP::load(const String& filepath) return ann; } -double ANN_MLP_ANNEAL::getAnnealInitialT() const -{ - const ANN_MLPImpl* this_ = dynamic_cast(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - return this_->getAnnealInitialT(); -} - -void ANN_MLP_ANNEAL::setAnnealInitialT(double val) -{ - ANN_MLPImpl* this_ = dynamic_cast< ANN_MLPImpl*>(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - this_->setAnnealInitialT(val); -} - -double ANN_MLP_ANNEAL::getAnnealFinalT() const -{ - const ANN_MLPImpl* this_ = dynamic_cast(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - return this_->getAnnealFinalT(); -} - -void ANN_MLP_ANNEAL::setAnnealFinalT(double val) -{ - ANN_MLPImpl* this_ = dynamic_cast(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - this_->setAnnealFinalT(val); -} - -double ANN_MLP_ANNEAL::getAnnealCoolingRatio() const -{ - const ANN_MLPImpl* this_ = dynamic_cast(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - return this_->getAnnealCoolingRatio(); -} - -void ANN_MLP_ANNEAL::setAnnealCoolingRatio(double val) -{ - ANN_MLPImpl* this_ = dynamic_cast< ANN_MLPImpl*>(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - this_->setAnnealInitialT(val); -} - -int ANN_MLP_ANNEAL::getAnnealItePerStep() const -{ - const ANN_MLPImpl* this_ = dynamic_cast(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - return this_->getAnnealItePerStep(); -} - -void ANN_MLP_ANNEAL::setAnnealItePerStep(int val) -{ - ANN_MLPImpl* this_ = dynamic_cast(this); - if (!this_) - CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL"); - this_->setAnnealInitialT(val); -} - }} /* End of file. */ diff --git a/modules/ml/test/test_mltests2.cpp b/modules/ml/test/test_mltests2.cpp index a193606..8dc7a2d 100644 --- a/modules/ml/test/test_mltests2.cpp +++ b/modules/ml/test/test_mltests2.cpp @@ -41,6 +41,8 @@ #include "test_precomp.hpp" +//#define GENERATE_TESTDATA + using namespace cv; using namespace std; @@ -248,7 +250,7 @@ TEST(ML_ANN, ActivationFunction) #endif } } -//#define GENERATE_TESTDATA + TEST(ML_ANN, Method) { String folder = string(cvtest::TS::ptr()->get_data_path()); @@ -275,6 +277,7 @@ TEST(ML_ANN, Method) methodName.push_back("_anneal"); // methodName.push_back("_backprop"); -----> NO BACKPROP TEST #ifdef GENERATE_TESTDATA + { Ptr xx = ml::ANN_MLP_ANNEAL::create(); Mat_ layerSizesXX(1, 4); layerSizesXX(0, 0) = tdata->getNVars(); @@ -290,6 +293,7 @@ TEST(ML_ANN, Method) fs.open(dataname + "_init_weight.yml.gz", FileStorage::WRITE + FileStorage::BASE64); xx->write(fs); fs.release(); + } #endif for (size_t i = 0; i < methodType.size(); i++) { @@ -300,6 +304,7 @@ TEST(ML_ANN, Method) x->setTrainMethod(methodType[i]); if (methodType[i] == ml::ANN_MLP::ANNEAL) { + x->setAnnealEnergyRNG(RNG(CV_BIG_INT(0xffffffff))); x->setAnnealInitialT(12); x->setAnnealFinalT(0.15); x->setAnnealCoolingRatio(0.96); diff --git a/samples/cpp/travelsalesman.cpp b/samples/cpp/travelsalesman.cpp index 138f61d..5a355b0 100644 --- a/samples/cpp/travelsalesman.cpp +++ b/samples/cpp/travelsalesman.cpp @@ -1,68 +1,62 @@ #include -using namespace std; using namespace cv; -void DrawTravelMap(Mat &img, vector &p, vector &n); - -class TravelSalesman : public ml::SimulatedAnnealingSolver +class TravelSalesman : public ml::SimulatedAnnealingSolverSystem { private : - vector &posCity; - vector &next; + const std::vector& posCity; + std::vector& next; RNG rng; int d0,d1,d2,d3; public: - - TravelSalesman(vector &p,vector &n):posCity(p),next(n) + TravelSalesman(std::vector &p, std::vector &n) : + posCity(p), next(n) { rng = theRNG(); - }; + } /** Give energy value for a state of system.*/ - virtual double energy(); + /*virtual*/ double energy() const; /** Function which change the state of system (random pertubation).*/ - virtual void changedState(); + /*virtual*/ void changeState(); /** Function to reverse to the previous state.*/ - virtual void reverseChangedState(); + /*virtual*/ void reverseState(); }; -void TravelSalesman::changedState() +void TravelSalesman::changeState() { d0 = rng.uniform(0,static_cast(posCity.size())); d1 = next[d0]; d2 = next[d1]; d3 = next[d2]; - int d0Tmp = d0; - int d1Tmp = d1; - int d2Tmp = d2; - next[d0Tmp] = d2; - next[d2Tmp] = d1; - next[d1Tmp] = d3; + next[d0] = d2; + next[d2] = d1; + next[d1] = d3; } -void TravelSalesman::reverseChangedState() +void TravelSalesman::reverseState() { next[d0] = d1; next[d1] = d2; next[d2] = d3; } -double TravelSalesman::energy() +double TravelSalesman::energy() const { - double e=0; + double e = 0; for (size_t i = 0; i < next.size(); i++) { - e += norm(posCity[i]-posCity[next[i]]); + e += norm(posCity[i]-posCity[next[i]]); } return e; } -void DrawTravelMap(Mat &img, vector &p, vector &n) +static void DrawTravelMap(Mat &img, std::vector &p, std::vector &n) { for (size_t i = 0; i < n.size(); i++) { @@ -74,12 +68,12 @@ int main(void) { int nbCity=40; Mat img(500,500,CV_8UC3,Scalar::all(0)); - RNG &rng=theRNG(); + RNG rng(123456); int radius=static_cast(img.cols*0.45); Point center(img.cols/2,img.rows/2); - vector posCity(nbCity); - vector next(nbCity); + std::vector posCity(nbCity); + std::vector next(nbCity); for (size_t i = 0; i < posCity.size(); i++) { double theta = rng.uniform(0., 2 * CV_PI); @@ -87,25 +81,33 @@ int main(void) posCity[i].y = static_cast(radius*sin(theta)) + center.y; next[i]=(i+1)%nbCity; } - TravelSalesman ts(posCity,next); + Ptr ts_system(new TravelSalesman(posCity, next)); + ml::SimulatedAnnealingSolver ts(ts_system); + + ts.setIterPerStep(10000*nbCity); ts.setCoolingRatio(0.99); ts.setInitialTemperature(100); - ts.setIterPerStep(10000*nbCity); ts.setFinalTemperature(100*0.97); DrawTravelMap(img,posCity,next); imshow("Map",img); waitKey(10); - for (int i = 0; i < 100; i++) + for (int i = 0, zeroChanges = 0; zeroChanges < 10; i++) { - ts.run(); + int changesApplied = ts.run(); img = Mat::zeros(img.size(),CV_8UC3); DrawTravelMap(img, posCity, next); imshow("Map", img); - waitKey(10); + int k = waitKey(10); double ti=ts.getFinalTemperature(); - cout< "<