MultipleClassifiers.cc
Go to the documentation of this file.
00001 
00006 #include "MultipleClassifiers.hh"
00007 
00008 
00009 
00010 MultipleClassifiers::MultipleClassifiers(int id, int modelType, int predType,
00011                                          int nModels, int trainMode,
00012                                          int trainFreq,
00013                                          float featPct, float expPct,
00014                                          float treeThreshold, bool stoch,
00015                                          float featRange, Random rng):
00016   id(id), modelType(modelType), predType(predType), nModels(nModels),
00017   mode(trainMode), freq(trainFreq),
00018   featPct(featPct), expPct(expPct), 
00019   treeThresh(treeThreshold), stoch(stoch), 
00020   addNoise(!stoch && (modelType == M5MULTI || modelType == M5SINGLE || modelType == M5ALLMULTI || modelType == M5ALLSINGLE || modelType == LSTMULTI || modelType == LSTSINGLE)),
00021   featRange(featRange),
00022   rng(rng)
00023 {
00024   STDEBUG = false;//true;
00025   ACC_DEBUG = false;//true;
00026   PRED_DEBUG = false; //true;
00027   CONF_DEBUG = false;
00028   COPYDEBUG = false;
00029   nsteps = 0;
00030   
00031   cout << "Created MultClass " << id << " with nModels: " << nModels << ", addNoise: " << addNoise << endl;
00032 
00033   for (int i = -1; i < id; i++)
00034     rng.uniform(0,1);
00035 
00036 
00037   initModels();
00038 
00039 }
00040 
00041 MultipleClassifiers::MultipleClassifiers(const MultipleClassifiers &t):
00042   id(t.id), modelType(t.modelType), predType(t.predType), nModels(t.nModels),
00043   mode(t.mode), freq(t.freq),
00044   featPct(t.featPct), expPct(t.expPct), 
00045   treeThresh(t.treeThresh), stoch(t.stoch), addNoise(t.addNoise),
00046   featRange(t.featRange), rng(t.rng)
00047 {
00048   COPYDEBUG = t.COPYDEBUG;
00049   if (COPYDEBUG) cout << "  MC copy constructor id " << id << endl;
00050   STDEBUG = t.STDEBUG;
00051   ACC_DEBUG = t.ACC_DEBUG;
00052   PRED_DEBUG = t.PRED_DEBUG;
00053   CONF_DEBUG = t.CONF_DEBUG;
00054   nsteps = t.nsteps;
00055 
00056   accuracy = t.accuracy;
00057 
00058   infos.resize(nModels);
00059   models.resize(nModels);
00060   if (COPYDEBUG) cout << "models size now " << models.size() << " nModels: " << nModels << endl;
00061   // copy each model
00062   for (unsigned i = 0; i < models.size(); i++){
00063     if (COPYDEBUG)cout << "MC copy model " << i << endl;
00064     models[i] = t.models[i]->getCopy();
00065   }
00066 
00067   if (COPYDEBUG) cout << "MC copy complete models size: " << models.size() << endl;
00068 }
00069 
00070 MultipleClassifiers* MultipleClassifiers::getCopy(){
00071   
00072   MultipleClassifiers* copy = new MultipleClassifiers(*this);
00073   return copy;
00074 
00075 }
00076 
00077 MultipleClassifiers::~MultipleClassifiers() {
00078   for (unsigned i = 0; i < models.size(); i++){
00079     delete models[i];
00080   }
00081   models.clear();
00082   accuracy.clear();
00083   infos.clear();
00084 }
00085 
00086 
00087 bool MultipleClassifiers::trainInstances(std::vector<classPair> &instances){
00088   if (STDEBUG) cout << id << " MultClass trainInstances: " << instances.size() << endl;
00089 
00090   bool changed = false;
00091   
00092   std::vector< std::vector<classPair> >subsets(nModels);
00093   for (int i = 0; i < nModels; i++){
00094     subsets[i].reserve(instances.size());
00095   }
00096 
00097   for (unsigned j = 0; j < instances.size(); j++){
00098     bool didUpdate = false;
00099     
00100     // train each model
00101     for (int i = 0; i < nModels; i++){
00102       
00103       // check accuracy of this model
00104       if (predType == BEST || predType == WEIGHTAVG){
00105         for (unsigned j = 0; j < instances.size(); j++){
00106           updateModelAccuracy(i, instances[j].in, instances[j].out);
00107         }
00108       }
00109 
00110       // create new vector with some random subset of experiences
00111       if (rng.uniform() < expPct){
00112         float origOutput = instances[j].out;
00113         if (addNoise) instances[j].out += rng.uniform(-0.2,0.2)*treeThresh;
00114         subsets[i].push_back(instances[j]);
00115         if (addNoise) instances[j].out = origOutput;
00116         didUpdate = true;
00117       }
00118     } // model loop
00119     
00120     // make sure someone got this instance
00121     if (!didUpdate){
00122       int model = rng.uniformDiscrete(0,nModels-1);
00123       //cout << "none got instance, update model " << model << endl;
00124       if (addNoise) instances[j].out += rng.uniform(-0.2,0.2)*treeThresh;
00125       subsets[model].push_back(instances[j]);
00126     }
00127   } // instances loop
00128     
00129   // now update models
00130   for (int i = 0; i < nModels; i++){
00131     if (subsets[i].size() > 0){
00132       if (STDEBUG) cout << id << " train model " << i << " on subset of size "
00133                         << subsets[i].size() << endl;
00134       bool singleChange = models[i]->trainInstances(subsets[i]);
00135       changed = changed || singleChange;
00136     }
00137   }
00138   
00139   nsteps += instances.size();
00140 
00141   return changed;
00142 }
00143 
00144 // here the target output will be a single value
00145 bool MultipleClassifiers::trainInstance(classPair &instance){
00146   if (STDEBUG) cout << id << " trainInstance" << endl;
00147 
00148   bool changed = false;
00149 
00150   // train each model
00151   bool didUpdate = false;
00152   for (int i = 0; i < nModels; i++){
00153 
00154     // check accuracy of this model
00155     if (predType == BEST || predType == WEIGHTAVG){
00156       updateModelAccuracy(i, instance.in, instance.out);
00157     }
00158 
00159     // update with new experience
00160     if (rng.uniform() < expPct){
00161       didUpdate = true;
00162       float origOutput = instance.out;
00163       if (addNoise) instance.out += rng.uniform(-0.2,0.2)*treeThresh;
00164       bool singleChange = models[i]->trainInstance(instance);
00165       if (addNoise) instance.out = origOutput;
00166       changed = changed || singleChange;
00167     }
00168   }
00169 
00170   // make sure some model got the transition
00171   if (!didUpdate){
00172     int model = rng.uniformDiscrete(0,nModels-1);
00173     if (addNoise) instance.out += rng.uniform(-0.2,0.2)*treeThresh;
00174     bool singleChange = models[model]->trainInstance(instance);
00175     changed = singleChange || changed;
00176   }
00177 
00178   nsteps++;
00179 
00180   return changed;
00181 
00182 }
00183 
00184 
00185 // get all the models outputs and combine them somehow
00186 void MultipleClassifiers::testInstance(const std::vector<float> &input, std::map<float, float>* retval){
00187   if (STDEBUG) cout << id << " testInstance" << endl;
00188 
00189   if ((int)infos.size() != nModels){
00190     infos.resize(nModels);
00191   }
00192 
00193   for (unsigned i = 0; i < infos.size(); i++){
00194     infos[i].clear();
00195   }
00196 
00197   retval->clear();
00198 
00199   // possibly have to init our model
00200   if ((int)models.size() != nModels)
00201     initModels();
00202 
00204   // return best
00205   if (predType == BEST){
00206     float acc = -1.0;
00207     int best = -1;
00208     for (int i = 0; i < nModels; i++){
00209       if (PRED_DEBUG) cout << "Model " << i << " has acc "
00210                            << accuracy[i] << endl;
00211       if (accuracy[i] > acc){
00212         acc = accuracy[i];
00213         best = i;
00214       }
00215     }
00216     if (PRED_DEBUG) cout << id << " Returning model " << best
00217                          << " with accuracy " << acc << endl;
00218     models[best]->testInstance(input, retval);
00219     return;
00220   }
00222 
00223 
00225   // calculate weights for weighted avg
00226   std::vector<float> weights(nModels, 1.0);
00227   if (predType == WEIGHTAVG){
00228     float accSum = 0.0;
00229     
00230     for (int j = 0; j < nModels; j++){
00231       accSum += accuracy[j];
00232     }
00233 
00234     if (accSum > 0.0){
00235       for (int j  = 0; j < nModels; j++){
00236         weights[j] = (float)nModels * accuracy[j] / accSum;
00237         if (PRED_DEBUG || ACC_DEBUG) cout << "Model " << j << " acc: "
00238                                           << accuracy[j] << " weight: "
00239                                           << weights[j] << endl;
00240       }
00241     }
00242   }
00244 
00245   // get state action info from each tree in our set
00246   for (int j = 0; j < nModels; j++){
00247     models[j]->testInstance(input, &(infos[j]));
00248   }
00249 
00250   // make a list of all outcomes any model predicted
00251   for (int i = 0; i < nModels; i++){
00252     for (std::map<float, float>::iterator it = infos[i].begin();
00253          it != infos[i].end(); it++){
00254 
00255       float outcome = (*it).first;
00256       float prob = (*it).second;
00257 
00258       // only if prob > 0
00259       if (prob > 0){
00260 
00261         // see if its not already in there
00262         if (!retval->count(outcome)){
00263           if (PRED_DEBUG) cout << " new outcome " << outcome << endl;
00264           // check if it really is a new one
00265           for (std::map<float, float>::iterator j = retval->begin();
00266                j != retval->end(); j++){
00267             if (fabs(outcome - j->first) < treeThresh){
00268               if (PRED_DEBUG) cout << "within treeThresh of " << j->first << endl;
00269               outcome = j->first;
00270               break;
00271             }
00272           }
00273         }
00274 
00275         (*retval)[outcome] += (prob * weights[i] / (float)nModels);
00276         
00277         if (PRED_DEBUG) cout << "inserting outcome " << outcome
00278                              << " from model " << i
00279                              << " with prob: " << prob
00280                              << " and weight: " << weights[i]
00281                              << " now total prob: " << (*retval)[outcome] << endl;
00282         
00283       }
00284     }
00285   }
00286 }
00287 
00288 
00289 
00290 float MultipleClassifiers::getConf(const std::vector<float> &input){
00291   if (STDEBUG || CONF_DEBUG) cout << id << " getConf" << endl;
00292 
00293   if ((int)infos.size() != nModels){
00294     infos.resize(nModels);
00295   }
00296 
00297   // get predictions if we haven't
00298   if (predType == BEST || predType == SEPARATE){
00299     if (CONF_DEBUG) cout << "conf: getting predictions again" << endl;
00300     for (int j = 0; j < nModels; j++){
00301       infos[j].clear();
00302       models[j]->testInstance(input, &(infos[j]));
00303     }
00304   }
00305 
00306   float conf = 0;
00307 
00308   // for deterministic trees, calculating a distribution of outcomes
00309   // calculate kl divergence
00310   if (modelType == RMAX || modelType == SLF || modelType == C45TREE ||
00311       modelType == STUMP ){ 
00312     conf = 1.0 - klDivergence(input);
00313   }
00314 
00315   // for continuous trees, providing a single continuous prediction
00316   // calcluate variance
00317   else {
00318     // use scaled sd
00319     conf = 1.0 - sqrt(variance(input)) / featRange;
00320   }
00321 
00322   if (CONF_DEBUG) cout << "return conf: " << conf << endl;
00323 
00324   return conf;
00325 
00326 }
00327 
00328 // calculate kl divergence of these predictions
00329 float MultipleClassifiers::klDivergence(const std::vector<float> &input){
00330 
00331   // KL divergence equation
00332   // D_KL(P||Q) = SUM_i P(i) log (P(i)/Q(i))
00333   float totalKL = 0.0;
00334 
00335   // I guess let's take the avg KL divergence of comparisons 
00336   // between all pairs of models
00337   for (int i = 0; i < nModels; i++){
00338     for (int j = 0; j < nModels; j++){
00339       if (i == j) continue;
00340       float singleKL = 0.0;
00341 
00342       for (std::map<float, float>::iterator it = infos[i].begin();
00343            it != infos[i].end(); it++){
00344         
00345         float outcome = (*it).first;
00346         float prob = (*it).second;
00347         
00348         if (prob == 0){
00349           continue;
00350         }
00351 
00352         if (CONF_DEBUG) 
00353           cout << "model " << i << " predicts " << outcome << " with prob " 
00354                << prob << ", model " << j << " has prob " 
00355                << infos[j][outcome] << endl;
00356 
00357         float jProb = infos[j][outcome];
00358         if (jProb == 0) jProb = 0.01;
00359 
00360         singleKL += prob * log(prob / jProb);
00361         
00362       }
00363       totalKL += singleKL;
00364 
00365       if (CONF_DEBUG) 
00366         cout << "D_KL(" << i << "||" << j << ") = " << singleKL
00367              << " klsum: " << totalKL << endl;
00368 
00369     } // outcomes of model i
00370   } // models
00371 
00372   int npairs = nModels * (nModels - 1);
00373   float avgKL = totalKL / (float)npairs;
00374 
00375   // normalize.  since my 0 prob is really 0.01, and log 1/0.01 = 4.60517, we'll divide by that
00376   avgKL /= 4.60517;
00377 
00378   if (CONF_DEBUG) cout << "AvgKL: " << avgKL << endl << endl;
00379 
00380   return avgKL;
00381 
00382 }
00383 
00384 // calculate the variance of these predictions
00385 float MultipleClassifiers::variance(const std::vector<float> &input){
00386 
00387   float sum = 0.0;
00388   float sumSqr = 0.0;
00389 
00390   for (int i = 0; i < nModels; i++){
00391     float val = infos[i].begin()->first;
00392     sum += val;
00393     sumSqr += (val*val);
00394   }
00395 
00396   float mean = sum / (float)nModels;
00397   float variance = (sumSqr - sum*mean) / (float)(nModels-1.0);
00398 
00399   if (CONF_DEBUG) cout << "variance of predictions is " << variance << endl;
00400 
00401   return variance;
00402 
00403 }
00404 
00405 // init models
00406 void MultipleClassifiers::initModels(){
00407   if (STDEBUG) cout << "initModels()" << endl;
00408 
00409   models.resize(nModels);
00410   accuracy.resize(nModels, 0.0);
00411 
00412   // init the trees or stumps
00413   for (int i = 0; i < nModels; i++){
00414 
00415     if (modelType == C45TREE){
00416       models[i] = new C45Tree(id + i*(1+nModels), mode, freq, 0, featPct, rng);
00417     }
00418     else if (modelType == M5MULTI){
00419       models[i] = new M5Tree(id + i*(1+nModels), mode, freq, 0, featPct, false, false, treeThresh, rng);
00420     }
00421     else if (modelType == M5ALLMULTI){
00422       models[i] = new M5Tree(id + i*(1+nModels), mode, freq, 0, featPct, false, true, treeThresh, rng);
00423     }
00424     else if (modelType == M5ALLSINGLE){
00425       models[i] = new M5Tree(id + i*(1+nModels), mode, freq, 0, featPct, true, true, treeThresh, rng);
00426     }
00427     else if (modelType == M5SINGLE){
00428       models[i] = new M5Tree(id + i*(1+nModels), mode, freq, 0, featPct, true, false, treeThresh, rng);
00429     }
00430     else if (modelType == LSTSINGLE){
00431       models[i] = new LinearSplitsTree(id + i*(1+nModels), mode, freq, 0, featPct, true, treeThresh, rng);
00432     }
00433     else if (modelType == LSTMULTI){
00434       models[i] = new LinearSplitsTree(id + i*(1+nModels), mode, freq, 0, featPct, false, treeThresh, rng);
00435     }
00436     else if (modelType == STUMP){
00437       models[i] = new Stump(id + 1*(1+nModels), mode, freq, 0, featPct, rng);
00438     }
00439     else if (modelType == ALLM5TYPES){
00440       // select an m5 type randomly.  so multivariate v single and allfeats v subtree feats
00441       bool simple = rng.bernoulli(0.5);
00442       bool allFeats = rng.bernoulli(0.5);
00443       //cout << "ALL types init tree " << i << " with simple: " << simple << " and allFeats: " << allFeats << endl;
00444       models[i] = new M5Tree(id + i*(1+nModels), mode, freq, 0, featPct, simple, allFeats, treeThresh, rng);
00445     }
00446     else {
00447       cout << "Invalid model type for this committee" << endl;
00448       exit(-1);
00449     }
00450   }
00451 
00452 }
00453 
00454 
00455 void MultipleClassifiers::updateModelAccuracy(int i, const std::vector<float> &in,
00456                                               float output){
00457 
00458   if (ACC_DEBUG) cout << "Check model accuracy " << i << " curr: " << accuracy[i] << endl;
00459 
00460   // get prediction
00461   std::map<float, float> pred;
00462   models[i]->testInstance(in, &pred);
00463 
00464   // see if it was correct
00465   bool correct = true;
00466   // check trans prob
00467   // for now, just see if prob of next was ML from this model
00468   float prob = pred[output];
00469   if (ACC_DEBUG) cout << "Model " << i << " predicted outcome "
00470                       << output << " with prob: " << prob << endl;
00471   if (prob <= 0.5)
00472     correct = false;
00473 
00474   // update accuracy
00475   accuracy[i] = accuracy[i] * (float)nsteps/(float)(nsteps+1.0);
00476   if (correct)
00477     accuracy[i] += 1.0/(float)(nsteps+1.0);
00478 
00479   if (ACC_DEBUG) cout << "Model " << i << " new accuracy: " << accuracy[i] << endl;
00480 
00481 }
00482 


rl_agent
Author(s): Todd Hester
autogenerated on Thu Jun 6 2019 22:00:13