From 45c1d052acf27ceeeec0fa2f474f620363bcbc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Montan=CC=83ana?= Date: Fri, 4 Aug 2023 01:35:45 +0200 Subject: [PATCH] Compile TANNew with poor accuracy --- src/BayesNet/Classifier.cc | 57 ++++++++++++++++++++++++++- src/BayesNet/Classifier.h | 2 + src/BayesNet/Network.cc | 3 -- src/BayesNet/TAN.cc | 4 -- src/BayesNet/TANNew.cc | 79 +++++++------------------------------- src/BayesNet/TANNew.h | 4 +- 6 files changed, 73 insertions(+), 76 deletions(-) diff --git a/src/BayesNet/Classifier.cc b/src/BayesNet/Classifier.cc index f68d1dc..aec1c6f 100644 --- a/src/BayesNet/Classifier.cc +++ b/src/BayesNet/Classifier.cc @@ -1,5 +1,6 @@ #include "Classifier.h" #include "bayesnetUtils.h" +#include "ArffFiles.h" namespace bayesnet { using namespace torch; @@ -12,7 +13,6 @@ namespace bayesnet { this->features = features; this->className = className; this->states = states; - cout << "Classifier samples: " << samples.sizes() << endl; checkFitParameters(); auto n_classes = states[className].size(); metrics = Metrics(samples, features, className, n_classes); @@ -115,8 +115,10 @@ namespace bayesnet { // Add all nodes to the network for (const auto& feature : features) { model.addNode(feature, states[feature].size()); + cout << "-Adding node " << feature << " with " << states[feature].size() << " states" << endl; } model.addNode(className, states[className].size()); + cout << "*Adding class " << className << " with " << states[className].size() << " states" << endl; } int Classifier::getNumberOfNodes() { @@ -139,4 +141,57 @@ namespace bayesnet { { model.dump_cpt(); } + void Classifier::localDiscretizationProposal(map& discretizers, Tensor& Xf) + { + // order of local discretization is important. no good 0, 1, 2... + auto order = model.topological_sort(); + auto& nodes = model.getNodes(); + vector indicesToReDiscretize; + auto n_samples = Xf.size(1); + bool upgrade = false; // Flag to check if we need to upgrade the model + for (auto feature : order) { + auto nodeParents = nodes[feature]->getParents(); + int index = find(features.begin(), features.end(), feature) - features.begin(); + vector parents; + transform(nodeParents.begin(), nodeParents.end(), back_inserter(parents), [](const auto& p) {return p->getName(); }); + if (parents.size() == 1) continue; // Only has class as parent + upgrade = true; + // Remove class as parent as it will be added later + parents.erase(remove(parents.begin(), parents.end(), className), parents.end()); + // Get the indices of the parents + vector indices; + transform(parents.begin(), parents.end(), back_inserter(indices), [&](const auto& p) {return find(features.begin(), features.end(), p) - features.begin(); }); + // Now we fit the discretizer of the feature conditioned on its parents and the class i.e. discretizer.fit(X[index], X[indices] + y) + vector yJoinParents; + transform(yv.begin(), yv.end(), back_inserter(yJoinParents), [&](const auto& p) {return to_string(p); }); + for (auto idx : indices) { + for (int i = 0; i < n_samples; ++i) { + yJoinParents[i] += to_string(Xv[idx][i]); + } + } + auto arff = ArffFiles(); + auto yxv = arff.factorize(yJoinParents); + auto xvf_ptr = Xf.index({ index }).data_ptr(); + auto xvf = vector(xvf_ptr, xvf_ptr + Xf.size(1)); + discretizers[feature]->fit(xvf, yxv); + indicesToReDiscretize.push_back(index); + } + if (upgrade) { + // Discretize again X (only the affected indices) with the new fitted discretizers + for (auto index : indicesToReDiscretize) { + auto Xt_ptr = Xf.index({ index }).data_ptr(); + auto Xt = vector(Xt_ptr, Xt_ptr + Xf.size(1)); + Xv[index] = discretizers[features[index]]->transform(Xt); + auto xStates = vector(discretizers[features[index]]->getCutPoints().size() + 1); + iota(xStates.begin(), xStates.end(), 0); + states[features[index]] = xStates; + } + // Now we fit the model again with the new values + cout << "Classifier: Upgrading model" << endl; + // To update the nodes states + addNodes(); + model.fit(Xv, yv, features, className); + cout << "Classifier: Model upgraded" << endl; + } + } } \ No newline at end of file diff --git a/src/BayesNet/Classifier.h b/src/BayesNet/Classifier.h index e10a523..baaabaf 100644 --- a/src/BayesNet/Classifier.h +++ b/src/BayesNet/Classifier.h @@ -4,6 +4,7 @@ #include "BaseClassifier.h" #include "Network.h" #include "BayesMetrics.h" +#include "CPPFImdlp.h" using namespace std; using namespace torch; @@ -26,6 +27,7 @@ namespace bayesnet { map> states; void checkFitParameters(); virtual void train() = 0; + void localDiscretizationProposal(map& discretizers, Tensor& Xf); public: Classifier(Network model); virtual ~Classifier() = default; diff --git a/src/BayesNet/Network.cc b/src/BayesNet/Network.cc index 7d18a7b..3a9d6a2 100644 --- a/src/BayesNet/Network.cc +++ b/src/BayesNet/Network.cc @@ -305,10 +305,7 @@ namespace bayesnet { map evidence; for (int i = 0; i < sample.size(0); ++i) { evidence[features[i]] = sample[i].item(); - cout << "Evidence: " << features[i] << " = " << sample[i].item() << endl; } - cout << "BEfore exact inference" << endl; - return exactInference(evidence); } double Network::computeFactor(map& completeEvidence) diff --git a/src/BayesNet/TAN.cc b/src/BayesNet/TAN.cc index 1757a2a..f47d7c5 100644 --- a/src/BayesNet/TAN.cc +++ b/src/BayesNet/TAN.cc @@ -19,10 +19,6 @@ namespace bayesnet { mi.push_back({ i, mi_value }); } sort(mi.begin(), mi.end(), [](const auto& left, const auto& right) {return left.second < right.second;}); - cout << "MI: " << endl; - for (int i = 0; i < mi.size(); ++i) { - cout << mi[i].first << " " << mi[i].second << endl; - } auto root = mi[mi.size() - 1].first; // 2. Compute mutual information between each feature and the class auto weights = metrics.conditionalEdge(); diff --git a/src/BayesNet/TANNew.cc b/src/BayesNet/TANNew.cc index 57be2e6..9f5c47b 100644 --- a/src/BayesNet/TANNew.cc +++ b/src/BayesNet/TANNew.cc @@ -1,93 +1,42 @@ #include "TANNew.h" -#include "ArffFiles.h" namespace bayesnet { using namespace std; - TANNew::TANNew() : TAN(), n_features{ 0 } {} + TANNew::TANNew() : TAN() {} TANNew::~TANNew() {} - TANNew& TANNew::fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) + TANNew& TANNew::fit(torch::Tensor& X_, torch::Tensor& y_, vector& features_, string className_, map>& states_) { - n_features = features.size(); - this->Xf = torch::transpose(X, 0, 1); // now it is mxn as X comes in nxm - this->y = y; - this->features = features; - this->className = className; + Xf = X_; + y = y_; + features = features_; + className = className_; Xv = vector>(); - auto Xvf = vector>(); yv = vector(y.data_ptr(), y.data_ptr() + y.size(0)); + // discretize input data by feature(row) for (int i = 0; i < features.size(); ++i) { auto* discretizer = new mdlp::CPPFImdlp(); - auto Xt_ptr = X.index({ i }).data_ptr(); - auto Xt = vector(Xt_ptr, Xt_ptr + X.size(1)); - Xvf.push_back(Xt); + auto Xt_ptr = Xf.index({ i }).data_ptr(); + auto Xt = vector(Xt_ptr, Xt_ptr + Xf.size(1)); discretizer->fit(Xt, yv); Xv.push_back(discretizer->transform(Xt)); auto xStates = vector(discretizer->getCutPoints().size() + 1); iota(xStates.begin(), xStates.end(), 0); - this->states[features[i]] = xStates; + states[features[i]] = xStates; discretizers[features[i]] = discretizer; } int n_classes = torch::max(y).item() + 1; auto yStates = vector(n_classes); iota(yStates.begin(), yStates.end(), 0); - this->states[className] = yStates; + states[className] = yStates; // Now we have standard TAN and now we implement the proposal // 1st we need to fit the model to build the TAN structure cout << "TANNew: Fitting model" << endl; - TAN::fit(Xv, yv, features, className, this->states); + TAN::fit(Xv, yv, features, className, states); cout << "TANNew: Model fitted" << endl; - // order of local discretization is important. no good 0, 1, 2... - auto edges = model.getEdges(); - auto order = model.topological_sort(); - auto& nodes = model.getNodes(); - vector indicesToReDiscretize; - bool upgrade = false; // Flag to check if we need to upgrade the model - for (auto feature : order) { - auto nodeParents = nodes[feature]->getParents(); - int index = find(features.begin(), features.end(), feature) - features.begin(); - vector parents; - transform(nodeParents.begin(), nodeParents.end(), back_inserter(parents), [](const auto& p) {return p->getName(); }); - if (parents.size() == 1) continue; // Only has class as parent - upgrade = true; - // Remove class as parent as it will be added later - parents.erase(remove(parents.begin(), parents.end(), className), parents.end()); - // Get the indices of the parents - vector indices; - transform(parents.begin(), parents.end(), back_inserter(indices), [&](const auto& p) {return find(features.begin(), features.end(), p) - features.begin(); }); - // Now we fit the discretizer of the feature conditioned on its parents and the class i.e. discretizer.fit(X[index], X[indices] + y) - vector yJoinParents; - transform(yv.begin(), yv.end(), back_inserter(yJoinParents), [&](const auto& p) {return to_string(p); }); - for (auto idx : indices) { - for (int i = 0; i < Xvf[idx].size(); ++i) { - yJoinParents[i] += to_string(Xv[idx][i]); - } - } - auto arff = ArffFiles(); - auto yxv = arff.factorize(yJoinParents); - discretizers[feature]->fit(Xvf[index], yxv); - indicesToReDiscretize.push_back(index); - } - if (upgrade) { - // Discretize again X (only the affected indices) with the new fitted discretizers - for (auto index : indicesToReDiscretize) { - auto Xt_ptr = X.index({ index }).data_ptr(); - auto Xt = vector(Xt_ptr, Xt_ptr + X.size(1)); - Xv[index] = discretizers[features[index]]->transform(Xt); - auto xStates = vector(discretizers[features[index]]->getCutPoints().size() + 1); - iota(xStates.begin(), xStates.end(), 0); - this->states[features[index]] = xStates; - } - // Now we fit the model again with the new values - cout << "TANNew: Upgrading model" << endl; - model.fit(Xv, yv, features, className); - cout << "TANNew: Model upgraded" << endl; - } + localDiscretizationProposal(discretizers, Xf); return *this; } - void TANNew::train() - { - TAN::train(); - } + Tensor TANNew::predict(Tensor& X) { auto Xtd = torch::zeros_like(X, torch::kInt32); diff --git a/src/BayesNet/TANNew.h b/src/BayesNet/TANNew.h index 5aecb23..e7501b1 100644 --- a/src/BayesNet/TANNew.h +++ b/src/BayesNet/TANNew.h @@ -8,12 +8,10 @@ namespace bayesnet { class TANNew : public TAN { private: map discretizers; - int n_features; - torch::Tensor Xf; // X continuous + torch::Tensor Xf; // X continuous nxm tensor public: TANNew(); virtual ~TANNew(); - void train() override; TANNew& fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) override; vector graph(const string& name = "TAN") override; Tensor predict(Tensor& X) override;