diff --git a/src/BayesNet/BaseClassifier.h b/src/BayesNet/BaseClassifier.h index 1bff6f6..0ae9a1d 100644 --- a/src/BayesNet/BaseClassifier.h +++ b/src/BayesNet/BaseClassifier.h @@ -10,6 +10,7 @@ namespace bayesnet { virtual BaseClassifier& fit(vector>& X, vector& y, vector& features, string className, map>& states) = 0; // X is nxm tensor, y is nx1 tensor virtual BaseClassifier& fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) = 0; + virtual ~BaseClassifier() = default; torch::Tensor virtual predict(torch::Tensor& X) = 0; vector virtual predict(vector>& X) = 0; float virtual score(vector>& X, vector& y) = 0; @@ -19,7 +20,6 @@ namespace bayesnet { int virtual getNumberOfStates() = 0; vector virtual show() = 0; vector virtual graph(const string& title = "") = 0; - virtual ~BaseClassifier() = default; const string inline getVersion() const { return "0.1.0"; }; vector virtual topological_order() = 0; void virtual dump_cpt() = 0; diff --git a/src/BayesNet/CMakeLists.txt b/src/BayesNet/CMakeLists.txt index 6fa1c24..2d4d729 100644 --- a/src/BayesNet/CMakeLists.txt +++ b/src/BayesNet/CMakeLists.txt @@ -1,4 +1,4 @@ include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp) include_directories(${BayesNet_SOURCE_DIR}/lib/Files) -add_library(BayesNet bayesnetUtils.cc Network.cc Node.cc BayesMetrics.cc Classifier.cc KDB.cc TAN.cc SPODE.cc Ensemble.cc AODE.cc TANNew.cc Mst.cc) +add_library(BayesNet bayesnetUtils.cc Network.cc Node.cc BayesMetrics.cc Classifier.cc KDB.cc TAN.cc SPODE.cc Ensemble.cc AODE.cc TANNew.cc Mst.cc Proposal.cc) target_link_libraries(BayesNet mdlp ArffFiles "${TORCH_LIBRARIES}") \ No newline at end of file diff --git a/src/BayesNet/Classifier.cc b/src/BayesNet/Classifier.cc index aec1c6f..26dce1d 100644 --- a/src/BayesNet/Classifier.cc +++ b/src/BayesNet/Classifier.cc @@ -1,6 +1,5 @@ #include "Classifier.h" #include "bayesnetUtils.h" -#include "ArffFiles.h" namespace bayesnet { using namespace torch; @@ -40,7 +39,6 @@ namespace bayesnet { // X is nxm where n is the number of features and m the number of samples Classifier& Classifier::fit(vector>& X, vector& y, vector& features, string className, map>& states) { - this->X = torch::zeros({ static_cast(X.size()), static_cast(X[0].size()) }, kInt32); Xv = X; for (int i = 0; i < X.size(); ++i) { @@ -141,57 +139,5 @@ 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 baaabaf..e10a523 100644 --- a/src/BayesNet/Classifier.h +++ b/src/BayesNet/Classifier.h @@ -4,7 +4,6 @@ #include "BaseClassifier.h" #include "Network.h" #include "BayesMetrics.h" -#include "CPPFImdlp.h" using namespace std; using namespace torch; @@ -27,7 +26,6 @@ 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 3a9d6a2..6248ab5 100644 --- a/src/BayesNet/Network.cc +++ b/src/BayesNet/Network.cc @@ -211,7 +211,10 @@ namespace bayesnet { result = torch::zeros({ samples.size(1), classNumStates }, torch::kFloat64); for (int i = 0; i < samples.size(1); ++i) { auto sample = samples.index({ "...", i }); - result.index_put_({ i, "..." }, torch::tensor(predict_sample(sample), torch::kFloat64)); + auto psample = predict_sample(sample); + auto temp = torch::tensor(psample, torch::kFloat64); +// result.index_put_({ i, "..." }, torch::tensor(predict_sample(sample), torch::kFloat64)); + result.index_put_({ i, "..." }, temp); } if (proba) return result; @@ -333,7 +336,6 @@ namespace bayesnet { for (auto& thread : threads) { thread.join(); } - // Normalize result double sum = accumulate(result.begin(), result.end(), 0.0); transform(result.begin(), result.end(), result.begin(), [sum](double& value) { return value / sum; }); diff --git a/src/BayesNet/Proposal.cc b/src/BayesNet/Proposal.cc index 8e18a2b..eff80d4 100644 --- a/src/BayesNet/Proposal.cc +++ b/src/BayesNet/Proposal.cc @@ -2,8 +2,14 @@ #include "ArffFiles.h" namespace bayesnet { - Proposal::Proposal(vector>& Xv_, vector& yv_) : Xv(Xv_), yv(yv_) {} - void Proposal::localDiscretizationProposal(Network& model, vector& features, string className, map>& states) + Proposal::Proposal(vector>& Xv_, vector& yv_, vector& features_, string& className_) : Xv(Xv_), yv(yv_), pFeatures(features_), pClassName(className_) {} + Proposal::~Proposal() + { + for (auto& [key, value] : discretizers) { + delete value; + } + } + void Proposal::localDiscretizationProposal(map>& states, Network& model) { // order of local discretization is important. no good 0, 1, 2... auto order = model.topological_sort(); @@ -13,16 +19,17 @@ namespace bayesnet { 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 + if (nodeParents.size() < 2) continue; // Only has class as parent upgrade = true; + int index = find(pFeatures.begin(), pFeatures.end(), feature) - pFeatures.begin(); + indicesToReDiscretize.push_back(index); + vector parents; + transform(nodeParents.begin(), nodeParents.end(), back_inserter(parents), [](const auto& p) { return p->getName(); }); // Remove class as parent as it will be added later - parents.erase(remove(parents.begin(), parents.end(), className), parents.end()); + parents.erase(remove(parents.begin(), parents.end(), pClassName), 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(); }); + transform(parents.begin(), parents.end(), back_inserter(indices), [&](const auto& p) {return find(pFeatures.begin(), pFeatures.end(), p) - pFeatures.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); }); @@ -36,27 +43,27 @@ namespace bayesnet { 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); + Xv[index] = discretizers[pFeatures[index]]->transform(Xt); + auto xStates = vector(discretizers[pFeatures[index]]->getCutPoints().size() + 1); iota(xStates.begin(), xStates.end(), 0); - states[features[index]] = xStates; + //Update new states of the feature/node + states[pFeatures[index]] = xStates; } - } } - void Proposal::fit_local_discretization(vector& features, string className, map>& states, torch::Tensor& y) + void Proposal::fit_local_discretization(map>& states, torch::Tensor& y) { + // Sharing Xv and yv with Classifier Xv = 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) { + for (int i = 0; i < pFeatures.size(); ++i) { auto* discretizer = new mdlp::CPPFImdlp(); auto Xt_ptr = Xf.index({ i }).data_ptr(); auto Xt = vector(Xt_ptr, Xt_ptr + Xf.size(1)); @@ -64,12 +71,12 @@ namespace bayesnet { Xv.push_back(discretizer->transform(Xt)); auto xStates = vector(discretizer->getCutPoints().size() + 1); iota(xStates.begin(), xStates.end(), 0); - states[features[i]] = xStates; - discretizers[features[i]] = discretizer; + states[pFeatures[i]] = xStates; + discretizers[pFeatures[i]] = discretizer; } int n_classes = torch::max(y).item() + 1; auto yStates = vector(n_classes); iota(yStates.begin(), yStates.end(), 0); - states[className] = yStates; + states[pClassName] = yStates; } } \ No newline at end of file diff --git a/src/BayesNet/Proposal.h b/src/BayesNet/Proposal.h index f92d9d2..981c22c 100644 --- a/src/BayesNet/Proposal.h +++ b/src/BayesNet/Proposal.h @@ -9,14 +9,16 @@ namespace bayesnet { class Proposal { public: - Proposal(vector>& Xv_, vector& yv_); - virtual ~Proposal() = default; + Proposal(vector>& Xv_, vector& yv_, vector& features_, string& className_); + virtual ~Proposal(); protected: - void localDiscretizationProposal(Network& model, vector& features, string className, map>& states); - void fit_local_discretization(vector& features, string className, map>& states, torch::Tensor& y); + void localDiscretizationProposal(map>& states, Network& model); + void fit_local_discretization(map>& states, torch::Tensor& y); torch::Tensor Xf; // X continuous nxm tensor map discretizers; private: + vector& pFeatures; + string& pClassName; vector>& Xv; // X discrete nxm vector vector& yv; }; diff --git a/src/BayesNet/TANNew.cc b/src/BayesNet/TANNew.cc index 9f5c47b..44b5658 100644 --- a/src/BayesNet/TANNew.cc +++ b/src/BayesNet/TANNew.cc @@ -2,41 +2,25 @@ namespace bayesnet { using namespace std; - TANNew::TANNew() : TAN() {} + TANNew::TANNew() : TAN(), Proposal(TAN::Xv, TAN::yv, TAN::features, TAN::className) {} TANNew::~TANNew() {} TANNew& TANNew::fit(torch::Tensor& X_, torch::Tensor& y_, vector& features_, string className_, map>& states_) { + // This first part should go in a Classifier method called fit_local_discretization o fit_float... + TAN::features = features_; + TAN::className = className_; Xf = X_; y = y_; - features = features_; - className = className_; - Xv = 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 = 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); - 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); - 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 + fit_local_discretization(states, y); + // We have discretized the input data + // 1st we need to fit the model to build the normal TAN structure, TAN::fit initializes the base Bayesian network cout << "TANNew: Fitting model" << endl; - TAN::fit(Xv, yv, features, className, states); + TAN::fit(TAN::Xv, TAN::yv, TAN::features, TAN::className, states); cout << "TANNew: Model fitted" << endl; - localDiscretizationProposal(discretizers, Xf); + //localDiscretizationProposal(states, model); + //addNodes(); return *this; } - 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 e7501b1..78e5f70 100644 --- a/src/BayesNet/TANNew.h +++ b/src/BayesNet/TANNew.h @@ -1,14 +1,12 @@ #ifndef TANNEW_H #define TANNEW_H #include "TAN.h" -#include "CPPFImdlp.h" +#include "Proposal.h" namespace bayesnet { using namespace std; - class TANNew : public TAN { + class TANNew : public TAN, public Proposal { private: - map discretizers; - torch::Tensor Xf; // X continuous nxm tensor public: TANNew(); virtual ~TANNew();