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;
00025 ACC_DEBUG = false;
00026 PRED_DEBUG = false;
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
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
00101 for (int i = 0; i < nModels; i++){
00102
00103
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
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 }
00119
00120
00121 if (!didUpdate){
00122 int model = rng.uniformDiscrete(0,nModels-1);
00123
00124 if (addNoise) instances[j].out += rng.uniform(-0.2,0.2)*treeThresh;
00125 subsets[model].push_back(instances[j]);
00126 }
00127 }
00128
00129
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
00145 bool MultipleClassifiers::trainInstance(classPair &instance){
00146 if (STDEBUG) cout << id << " trainInstance" << endl;
00147
00148 bool changed = false;
00149
00150
00151 bool didUpdate = false;
00152 for (int i = 0; i < nModels; i++){
00153
00154
00155 if (predType == BEST || predType == WEIGHTAVG){
00156 updateModelAccuracy(i, instance.in, instance.out);
00157 }
00158
00159
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
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
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
00200 if ((int)models.size() != nModels)
00201 initModels();
00202
00204
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
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
00246 for (int j = 0; j < nModels; j++){
00247 models[j]->testInstance(input, &(infos[j]));
00248 }
00249
00250
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
00259 if (prob > 0){
00260
00261
00262 if (!retval->count(outcome)){
00263 if (PRED_DEBUG) cout << " new outcome " << outcome << endl;
00264
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
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
00309
00310 if (modelType == RMAX || modelType == SLF || modelType == C45TREE ||
00311 modelType == STUMP ){
00312 conf = 1.0 - klDivergence(input);
00313 }
00314
00315
00316
00317 else {
00318
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
00329 float MultipleClassifiers::klDivergence(const std::vector<float> &input){
00330
00331
00332
00333 float totalKL = 0.0;
00334
00335
00336
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 }
00370 }
00371
00372 int npairs = nModels * (nModels - 1);
00373 float avgKL = totalKL / (float)npairs;
00374
00375
00376 avgKL /= 4.60517;
00377
00378 if (CONF_DEBUG) cout << "AvgKL: " << avgKL << endl << endl;
00379
00380 return avgKL;
00381
00382 }
00383
00384
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
00406 void MultipleClassifiers::initModels(){
00407 if (STDEBUG) cout << "initModels()" << endl;
00408
00409 models.resize(nModels);
00410 accuracy.resize(nModels, 0.0);
00411
00412
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
00441 bool simple = rng.bernoulli(0.5);
00442 bool allFeats = rng.bernoulli(0.5);
00443
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
00461 std::map<float, float> pred;
00462 models[i]->testInstance(in, &pred);
00463
00464
00465 bool correct = true;
00466
00467
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
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