From 0c226371ccd390245e5cbc9a59cea841e1bfdd23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Monta=C3=B1ana?= Date: Sun, 23 Jul 2023 14:10:28 +0200 Subject: [PATCH] Ensemble Experiment, Folding, Classifiers and Network together --- src/BayesNet/BaseClassifier.h | 2 ++ src/BayesNet/Classifier.cc | 15 ++++++++++++++- src/BayesNet/Classifier.h | 11 ++++++----- src/BayesNet/Ensemble.cc | 22 +++++++++++++++++++++ src/BayesNet/Ensemble.h | 3 ++- src/BayesNet/Network.cc | 9 +++++++-- src/BayesNet/Network.h | 1 + src/Platform/Experiment.cc | 26 +++++++++++++++---------- src/Platform/Folding.cc | 11 +++++++++++ src/Platform/Folding.h | 6 ++++++ src/Platform/platformUtils.cc | 36 +++++++++++++++++++++++++---------- src/Platform/platformUtils.h | 5 +++-- 12 files changed, 116 insertions(+), 31 deletions(-) diff --git a/src/BayesNet/BaseClassifier.h b/src/BayesNet/BaseClassifier.h index 3d69dfb..2921f11 100644 --- a/src/BayesNet/BaseClassifier.h +++ b/src/BayesNet/BaseClassifier.h @@ -7,8 +7,10 @@ namespace bayesnet { class BaseClassifier { public: virtual BaseClassifier& fit(vector>& X, vector& y, vector& features, string className, map>& states) = 0; + virtual BaseClassifier& fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) = 0; vector virtual predict(vector>& X) = 0; float virtual score(vector>& X, vector& y) = 0; + float virtual score(torch::Tensor& X, torch::Tensor& y) = 0; vector virtual show() = 0; vector virtual graph(string title = "") = 0; virtual ~BaseClassifier() = default; diff --git a/src/BayesNet/Classifier.cc b/src/BayesNet/Classifier.cc index cc11a5d..6d0519a 100644 --- a/src/BayesNet/Classifier.cc +++ b/src/BayesNet/Classifier.cc @@ -15,10 +15,23 @@ namespace bayesnet { auto n_classes = states[className].size(); metrics = Metrics(dataset, features, className, n_classes); train(); - model.fit(Xv, yv, features, className); + if (Xv == vector>()) { + model.fit(X, y, features, className); + } else { + model.fit(Xv, yv, features, className); + } fitted = true; return *this; } + Classifier& Classifier::fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) + { + this->X = X; + this->y = y; + Xv = vector>(); + yv = vector(y.data_ptr(), y.data_ptr() + y.size(0)); + return build(features, className, states); + } + Classifier& Classifier::fit(vector>& X, vector& y, vector& features, string className, map>& states) { this->X = torch::zeros({ static_cast(X[0].size()), static_cast(X.size()) }, kInt64); diff --git a/src/BayesNet/Classifier.h b/src/BayesNet/Classifier.h index 0a44b02..578b9ed 100644 --- a/src/BayesNet/Classifier.h +++ b/src/BayesNet/Classifier.h @@ -29,15 +29,16 @@ namespace bayesnet { public: Classifier(Network model); virtual ~Classifier() = default; - Classifier& fit(vector>& X, vector& y, vector& features, string className, map>& states); + Classifier& fit(vector>& X, vector& y, vector& features, string className, map>& states) override; + Classifier& fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) override; void addNodes(); int getNumberOfNodes(); int getNumberOfEdges(); Tensor predict(Tensor& X); - vector predict(vector>& X); - float score(Tensor& X, Tensor& y); - float score(vector>& X, vector& y); - vector show(); + vector predict(vector>& X) override; + float score(Tensor& X, Tensor& y) override; + float score(vector>& X, vector& y) override; + vector show() override; }; } #endif diff --git a/src/BayesNet/Ensemble.cc b/src/BayesNet/Ensemble.cc index 6f389bc..59fdf91 100644 --- a/src/BayesNet/Ensemble.cc +++ b/src/BayesNet/Ensemble.cc @@ -22,6 +22,14 @@ namespace bayesnet { fitted = true; return *this; } + Ensemble& Ensemble::fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) + { + this->X = X; + this->y = y; + Xv = vector>(); + yv = vector(y.data_ptr(), y.data_ptr() + y.size(0)); + return build(features, className, states); + } Ensemble& Ensemble::fit(vector>& X, vector& y, vector& features, string className, map>& states) { this->X = torch::zeros({ static_cast(X[0].size()), static_cast(X.size()) }, kInt64); @@ -75,6 +83,20 @@ namespace bayesnet { } return voting(y_pred); } + float Ensemble::score(Tensor& X, Tensor& y) + { + if (!fitted) { + throw logic_error("Ensemble has not been fitted"); + } + auto y_pred = predict(X); + int correct = 0; + for (int i = 0; i < y_pred.size(0); ++i) { + if (y_pred[i].item() == y[i].item()) { + correct++; + } + } + return (double)correct / y_pred.size(0); + } float Ensemble::score(vector>& X, vector& y) { if (!fitted) { diff --git a/src/BayesNet/Ensemble.h b/src/BayesNet/Ensemble.h index 3ea9ad3..e25ee75 100644 --- a/src/BayesNet/Ensemble.h +++ b/src/BayesNet/Ensemble.h @@ -31,9 +31,10 @@ namespace bayesnet { Ensemble(); virtual ~Ensemble() = default; Ensemble& fit(vector>& X, vector& y, vector& features, string className, map>& states) override; + Ensemble& fit(torch::Tensor& X, torch::Tensor& y, vector& features, string className, map>& states) override; Tensor predict(Tensor& X); vector predict(vector>& X) override; - float score(Tensor& X, Tensor& y); + float score(Tensor& X, Tensor& y) override; float score(vector>& X, vector& y) override; vector show() override; vector graph(string title) override; diff --git a/src/BayesNet/Network.cc b/src/BayesNet/Network.cc index a4f678d..d7879a2 100644 --- a/src/BayesNet/Network.cc +++ b/src/BayesNet/Network.cc @@ -1,6 +1,7 @@ #include #include #include "Network.h" +#include "bayesnetUtils.h" namespace bayesnet { Network::Network() : laplaceSmoothing(1), features(vector()), className(""), classNumStates(0), maxThreads(0.8), fitted(false) {} Network::Network(float maxT) : laplaceSmoothing(1), features(vector()), className(""), classNumStates(0), maxThreads(maxT), fitted(false) {} @@ -8,7 +9,7 @@ namespace bayesnet { Network::Network(Network& other) : laplaceSmoothing(other.laplaceSmoothing), features(other.features), className(other.className), classNumStates(other.getClassNumStates()), maxThreads(other.getmaxThreads()), fitted(other.fitted) { for (auto& pair : other.nodes) { - nodes[pair.first] = make_unique(*pair.second); + nodes[pair.first] = std::make_unique(*pair.second); } } float Network::getmaxThreads() @@ -29,7 +30,7 @@ namespace bayesnet { nodes[name]->setNumStates(numStates); return; } - nodes[name] = make_unique(name, numStates); + nodes[name] = std::make_unique(name, numStates); } vector Network::getFeatures() { @@ -93,6 +94,10 @@ namespace bayesnet { { return nodes; } + void Network::fit(torch::Tensor& X, torch::Tensor& y, const vector& featureNames, const string& className) + { + this->fit(tensorToVector(X), vector(y.data_ptr(), y.data_ptr() + y.size(0)), featureNames, className); + } void Network::fit(const vector>& input_data, const vector& labels, const vector& featureNames, const string& className) { features = featureNames; diff --git a/src/BayesNet/Network.h b/src/BayesNet/Network.h index e7e8b68..d16e53c 100644 --- a/src/BayesNet/Network.h +++ b/src/BayesNet/Network.h @@ -40,6 +40,7 @@ namespace bayesnet { int getClassNumStates(); string getClassName(); void fit(const vector>&, const vector&, const vector&, const string&); + void fit(torch::Tensor&, torch::Tensor&, const vector&, const string&); vector predict(const vector>&); //Computes the conditional edge weight of variable index u and v conditioned on class_node torch::Tensor conditionalEdgeWeight(); diff --git a/src/Platform/Experiment.cc b/src/Platform/Experiment.cc index 60b8dec..f011f40 100644 --- a/src/Platform/Experiment.cc +++ b/src/Platform/Experiment.cc @@ -17,16 +17,19 @@ using namespace std; -pair cross_validation(Fold* fold, bayesnet::BaseClassifier* model, Tensor& X, Tensor& y, int k) +pair cross_validation(Fold* fold, bayesnet::BaseClassifier* model, Tensor& X, Tensor& y, vector features, string className, map> states) { + auto k = fold->getNumberOfFolds(); float accuracy = 0.0; for (int i = 0; i < k; i++) { auto [train, test] = fold->getFold(i); - auto X_train = X.indices{ train }; - auto y_train = y.indices{ train }; - auto X_test = X.indices{ test }; - auto y_test = y.indices{ test }; - model->fit(X_train, y_train); + auto train_t = torch::tensor(train); + auto test_t = torch::tensor(test); + auto X_train = X.index({ train_t }); + auto y_train = y.index({ train_t }); + auto X_test = X.index({ test_t }); + auto y_test = y.index({ test_t }); + model->fit(X_train, y_train, features, className, states); auto acc = model->score(X_test, y_test); accuracy += acc; } @@ -97,9 +100,12 @@ int main(int argc, char** argv) /* * Begin Processing */ - auto [X, y, features] = loadDataset(file_name, discretize_dataset); + auto [X, y, features, className] = loadDataset(file_name, discretize_dataset, class_last); + auto states = map>(); if (discretize_dataset) { - auto [discretized, maxes] = discretize(X, y, features); + auto [Xd, maxes] = discretizeTorch(X, y, features); + states = get_states(Xd, y, features, className); + X = Xd; } auto fold = StratifiedKFold(5, y, -1); auto classifiers = map({ @@ -108,7 +114,7 @@ int main(int argc, char** argv) } ); bayesnet::BaseClassifier* model = classifiers[model_name]; - auto results = cross_validation(model, X, y, fold, 5); + auto results = cross_validation(&fold, model, X, y, features, className, states); cout << "Accuracy: " << results.first << endl; return 0; -} \ No newline at end of file +} diff --git a/src/Platform/Folding.cc b/src/Platform/Folding.cc index 9fd1d03..2f1533b 100644 --- a/src/Platform/Folding.cc +++ b/src/Platform/Folding.cc @@ -28,10 +28,21 @@ pair, vector> KFold::getFold(int nFold) } return { train, test }; } +StratifiedKFold::StratifiedKFold(int k, torch::Tensor& y, int seed) : Fold(k, y.numel(), seed) +{ + n = y.numel(); + this->y = vector(y.data_ptr(), y.data_ptr() + n); + build(); +} StratifiedKFold::StratifiedKFold(int k, const vector& y, int seed) : Fold(k, y.size(), seed) { + this->y = y; n = y.size(); + build(); +} +void StratifiedKFold::build() +{ stratified_indices = vector>(k); int fold_size = n / k; int remainder = n % k; diff --git a/src/Platform/Folding.h b/src/Platform/Folding.h index b12af07..4e37ee1 100644 --- a/src/Platform/Folding.h +++ b/src/Platform/Folding.h @@ -1,5 +1,6 @@ #ifndef FOLDING_H #define FOLDING_H +#include #include using namespace std; @@ -12,6 +13,7 @@ public: Fold(int k, int n, int seed = -1) : k(k), n(n), seed(seed) {} virtual pair, vector> getFold(int nFold) = 0; virtual ~Fold() = default; + int getNumberOfFolds() { return k; } }; class KFold : public Fold { private: @@ -21,9 +23,13 @@ public: pair, vector> getFold(int nFold); }; class StratifiedKFold : public Fold { +private: + vector y; vector> stratified_indices; + void build(); public: StratifiedKFold(int k, const vector& y, int seed = -1); + StratifiedKFold(int k, torch::Tensor& y, int seed = -1); pair, vector> getFold(int nFold); }; #endif \ No newline at end of file diff --git a/src/Platform/platformUtils.cc b/src/Platform/platformUtils.cc index 2b18635..6f64858 100644 --- a/src/Platform/platformUtils.cc +++ b/src/Platform/platformUtils.cc @@ -15,6 +15,22 @@ pair, map> discretize(vector> discretizeTorch(Tensor& X, Tensor& y, vector features) +{ + map maxes; + auto fimdlp = mdlp::CPPFImdlp(); + auto Xd = torch::zeros_like(X, torch::kInt64); + auto yv = vector(y.data_ptr(), y.data_ptr() + y.size(0)); + for (int i = 0; i < X.size(1); i++) { + auto xv = vector(X.select(1, i).data_ptr(), X.select(1, i).data_ptr() + X.size(0)); + fimdlp.fit(xv, yv); + auto xdv = fimdlp.transform(xv); + auto xd = torch::tensor(xdv, torch::kInt64); + maxes[features[i]] = xd.max().item() + 1; + Xd.index_put_({ "...", i }, xd); + } + return { Xd, maxes }; +} vector discretizeDataset(vector& X, mdlp::labels_t& y) { @@ -38,10 +54,10 @@ bool file_exists(const std::string& name) } } -tuple < Tensor, Tensor, vector> loadDataset(string name, bool discretize) +tuple < Tensor, Tensor, vector, string> loadDataset(string name, bool discretize, bool class_last) { auto handler = ArffFiles(); - handler.load(PATH + static_cast(name) + ".arff"); + handler.load(PATH + static_cast(name) + ".arff", class_last); // Get Dataset X, y vector& X = handler.getX(); mdlp::labels_t& y = handler.getY(); @@ -64,20 +80,20 @@ tuple < Tensor, Tensor, vector> loadDataset(string name, bool discretize Xd.index_put_({ "...", i }, torch::tensor(X[i], torch::kFloat64)); } } - return { Xd, torch::tensor(y, torch::kInt64), features }; + return { Xd, torch::tensor(y, torch::kInt64), features, className }; } -pair , map>> discretize_info(Tensor& X, Tensor& y, vector features, string className) +map> get_states(Tensor& X, Tensor& y, vector features, string className) { - map maxes; + int max; map> states; for (int i = 0; i < X.size(1); i++) { - maxes[features[i]] = X.select(1, i).max().item() + 1; - states[features[i]] = vector(maxes[features[i]]); + max = X.select(1, i).max().item() + 1; + states[features[i]] = vector(max); } - maxes[className] = y.max().item() + 1; - states[className] = vector(maxes[className]); - return { maxes, states }; + max = y.max().item() + 1; + states[className] = vector(max); + return states; } tuple>, vector, vector, string, map>> loadFile(string name) diff --git a/src/Platform/platformUtils.h b/src/Platform/platformUtils.h index 80b198f..9eefd66 100644 --- a/src/Platform/platformUtils.h +++ b/src/Platform/platformUtils.h @@ -12,7 +12,8 @@ const string PATH = "../../data/"; bool file_exists(const std::string& name); pair, map> discretize(vector& X, mdlp::labels_t& y, vector features); +pair> discretizeTorch(torch::Tensor& X, torch::Tensor& y, vector features); tuple>, vector, vector, string, map>> loadFile(string name); -tuple> loadDataset(string name, bool discretize); -pair , map>> discretize_info(torch::Tensor& X, torch::Tensor& y); +tuple, string> loadDataset(string name, bool discretize, bool class_last); +map> get_states(torch::Tensor& X, torch::Tensor& y, vector features, string className); #endif //PLATFORM_UTILS_H