Compare commits

...

6 Commits

27 changed files with 138 additions and 202 deletions

View File

@@ -3,5 +3,5 @@ include_directories(${BayesNet_SOURCE_DIR}/src/BayesNet)
include_directories(${BayesNet_SOURCE_DIR}/lib/Files) include_directories(${BayesNet_SOURCE_DIR}/lib/Files)
include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp) include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp)
include_directories(${BayesNet_SOURCE_DIR}/lib/argparse/include) include_directories(${BayesNet_SOURCE_DIR}/lib/argparse/include)
add_executable(BayesNetSample sample.cc ${BayesNet_SOURCE_DIR}/src/Platform/Folding.cc) add_executable(BayesNetSample sample.cc ${BayesNet_SOURCE_DIR}/src/Platform/Folding.cc ${BayesNet_SOURCE_DIR}/src/Platform/Models.cc)
target_link_libraries(BayesNetSample BayesNet ArffFiles mdlp "${TORCH_LIBRARIES}") target_link_libraries(BayesNetSample BayesNet ArffFiles mdlp "${TORCH_LIBRARIES}")

View File

@@ -4,16 +4,12 @@
#include <thread> #include <thread>
#include <map> #include <map>
#include <argparse/argparse.hpp> #include <argparse/argparse.hpp>
#include "BaseClassifier.h"
#include "ArffFiles.h" #include "ArffFiles.h"
#include "Network.h"
#include "BayesMetrics.h" #include "BayesMetrics.h"
#include "CPPFImdlp.h" #include "CPPFImdlp.h"
#include "KDB.h"
#include "SPODE.h"
#include "AODE.h"
#include "TAN.h"
#include "Folding.h" #include "Folding.h"
#include "Models.h"
#include "modelRegister.h"
using namespace std; using namespace std;
@@ -73,9 +69,8 @@ int main(int argc, char** argv)
{"mfeat-factors", true}, {"mfeat-factors", true},
}; };
auto valid_datasets = vector<string>(); auto valid_datasets = vector<string>();
for (auto dataset : datasets) { transform(datasets.begin(), datasets.end(), back_inserter(valid_datasets),
valid_datasets.push_back(dataset.first); [](const pair<string, bool>& pair) { return pair.first; });
}
argparse::ArgumentParser program("BayesNetSample"); argparse::ArgumentParser program("BayesNetSample");
program.add_argument("-d", "--dataset") program.add_argument("-d", "--dataset")
.help("Dataset file name") .help("Dataset file name")
@@ -91,13 +86,13 @@ int main(int argc, char** argv)
.default_value(string{ PATH } .default_value(string{ PATH }
); );
program.add_argument("-m", "--model") program.add_argument("-m", "--model")
.help("Model to use {AODE, KDB, SPODE, TAN}") .help("Model to use " + platform::Models::instance()->toString())
.action([](const std::string& value) { .action([](const std::string& value) {
static const vector<string> choices = { "AODE", "KDB", "SPODE", "TAN" }; static const vector<string> choices = platform::Models::instance()->getNames();
if (find(choices.begin(), choices.end(), value) != choices.end()) { if (find(choices.begin(), choices.end(), value) != choices.end()) {
return value; return value;
} }
throw runtime_error("Model must be one of {AODE, KDB, SPODE, TAN}"); throw runtime_error("Model must be one of " + platform::Models::instance()->toString());
} }
); );
program.add_argument("--discretize").help("Discretize input dataset").default_value(false).implicit_value(true); program.add_argument("--discretize").help("Discretize input dataset").default_value(false).implicit_value(true);
@@ -153,9 +148,9 @@ int main(int argc, char** argv)
// Get className & Features // Get className & Features
auto className = handler.getClassName(); auto className = handler.getClassName();
vector<string> features; vector<string> features;
for (auto feature : handler.getAttributes()) { auto attributes = handler.getAttributes();
features.push_back(feature.first); transform(attributes.begin(), attributes.end(), back_inserter(features),
} [](const pair<string, string>& item) { return item.first; });
// Discretize Dataset // Discretize Dataset
auto [Xd, maxes] = discretize(X, y, features); auto [Xd, maxes] = discretize(X, y, features);
maxes[className] = *max_element(y.begin(), y.end()) + 1; maxes[className] = *max_element(y.begin(), y.end()) + 1;
@@ -164,12 +159,7 @@ int main(int argc, char** argv)
states[feature] = vector<int>(maxes[feature]); states[feature] = vector<int>(maxes[feature]);
} }
states[className] = vector<int>(maxes[className]); states[className] = vector<int>(maxes[className]);
auto classifiers = map<string, bayesnet::BaseClassifier*>({ auto clf = platform::Models::instance()->create(model_name);
{ "AODE", new bayesnet::AODE() }, { "KDB", new bayesnet::KDB(2) },
{ "SPODE", new bayesnet::SPODE(2) }, { "TAN", new bayesnet::TAN() }
}
);
bayesnet::BaseClassifier* clf = classifiers[model_name];
clf->fit(Xd, y, features, className, states); clf->fit(Xd, y, features, className, states);
auto score = clf->score(Xd, y); auto score = clf->score(Xd, y);
auto lines = clf->show(); auto lines = clf->show();

View File

@@ -12,8 +12,8 @@ namespace bayesnet {
: features(features) : features(features)
, className(className) , className(className)
, classNumStates(classNumStates) , classNumStates(classNumStates)
, samples(torch::zeros({ static_cast<int>(vsamples[0].size()), static_cast<int>(vsamples.size() + 1) }, torch::kInt32))
{ {
samples = torch::zeros({ static_cast<int>(vsamples[0].size()), static_cast<int>(vsamples.size() + 1) }, torch::kInt32);
for (int i = 0; i < vsamples.size(); ++i) { for (int i = 0; i < vsamples.size(); ++i) {
samples.index_put_({ "...", i }, torch::tensor(vsamples[i], torch::kInt32)); samples.index_put_({ "...", i }, torch::tensor(vsamples[i], torch::kInt32));
} }
@@ -123,7 +123,6 @@ namespace bayesnet {
*/ */
vector<pair<int, int>> Metrics::maximumSpanningTree(vector<string> features, Tensor& weights, int root) vector<pair<int, int>> Metrics::maximumSpanningTree(vector<string> features, Tensor& weights, int root)
{ {
auto result = vector<pair<int, int>>();
auto mst = MST(features, weights, root); auto mst = MST(features, weights, root);
return mst.maximumSpanningTree(); return mst.maximumSpanningTree();
} }

View File

@@ -11,7 +11,7 @@ namespace bayesnet {
Tensor samples; Tensor samples;
vector<string> features; vector<string> features;
string className; string className;
int classNumStates; int classNumStates = 0;
public: public:
Metrics() = default; Metrics() = default;
Metrics(Tensor&, vector<string>&, string&, int); Metrics(Tensor&, vector<string>&, string&, int);

View File

@@ -125,7 +125,6 @@ namespace bayesnet {
} }
void Classifier::addNodes() void Classifier::addNodes()
{ {
auto test = model.getEdges();
// Add all nodes to the network // Add all nodes to the network
for (auto feature : features) { for (auto feature : features) {
model.addNode(feature, states[feature].size()); model.addNode(feature, states[feature].size());

View File

@@ -148,10 +148,10 @@ namespace bayesnet {
} }
int Ensemble::getNumberOfStates() int Ensemble::getNumberOfStates()
{ {
int states = 0; int nstates = 0;
for (auto i = 0; i < n_models; ++i) { for (auto i = 0; i < n_models; ++i) {
states += models[i]->getNumberOfStates(); nstates += models[i]->getNumberOfStates();
} }
return states; return nstates;
} }
} }

View File

@@ -13,7 +13,7 @@ namespace bayesnet {
protected: protected:
void train() override; void train() override;
public: public:
KDB(int k, float theta = 0.03); explicit KDB(int k, float theta = 0.03);
virtual ~KDB() {}; virtual ~KDB() {};
vector<string> graph(string name = "KDB") override; vector<string> graph(string name = "KDB") override;
}; };

View File

@@ -7,9 +7,8 @@
namespace bayesnet { namespace bayesnet {
using namespace std; using namespace std;
Graph::Graph(int V) Graph::Graph(int V) : V(V), parent(vector<int>(V))
{ {
parent = vector<int>(V);
for (int i = 0; i < V; i++) for (int i = 0; i < V; i++)
parent[i] = i; parent[i] = i;
G.clear(); G.clear();
@@ -34,10 +33,10 @@ namespace bayesnet {
} }
void Graph::kruskal_algorithm() void Graph::kruskal_algorithm()
{ {
int i, uSt, vEd;
// sort the edges ordered on decreasing weight // sort the edges ordered on decreasing weight
sort(G.begin(), G.end(), [](auto& left, auto& right) {return left.first > right.first;}); sort(G.begin(), G.end(), [](const auto& left, const auto& right) {return left.first > right.first;});
for (i = 0; i < G.size(); i++) { for (int i = 0; i < G.size(); i++) {
int uSt, vEd;
uSt = find_set(G[i].second.first); uSt = find_set(G[i].second.first);
vEd = find_set(G[i].second.second); vEd = find_set(G[i].second.second);
if (uSt != vEd) { if (uSt != vEd) {

View File

@@ -10,7 +10,7 @@ namespace bayesnet {
private: private:
Tensor weights; Tensor weights;
vector<string> features; vector<string> features;
int root; int root = 0;
public: public:
MST() = default; MST() = default;
MST(vector<string>& features, Tensor& weights, int root); MST(vector<string>& features, Tensor& weights, int root);
@@ -23,7 +23,7 @@ namespace bayesnet {
vector <pair<float, pair<int, int>>> T; // vector for mst vector <pair<float, pair<int, int>>> T; // vector for mst
vector<int> parent; vector<int> parent;
public: public:
Graph(int V); explicit Graph(int V);
void addEdge(int u, int v, float wt); void addEdge(int u, int v, float wt);
int find_set(int i); int find_set(int i);
void union_set(int u, int v); void union_set(int u, int v);

View File

@@ -8,7 +8,7 @@ namespace bayesnet {
Network::Network(float maxT, int smoothing) : laplaceSmoothing(smoothing), features(vector<string>()), className(""), classNumStates(0), maxThreads(maxT), fitted(false) {} Network::Network(float maxT, int smoothing) : laplaceSmoothing(smoothing), features(vector<string>()), className(""), classNumStates(0), maxThreads(maxT), fitted(false) {}
Network::Network(Network& other) : laplaceSmoothing(other.laplaceSmoothing), features(other.features), className(other.className), classNumStates(other.getClassNumStates()), maxThreads(other.getmaxThreads()), fitted(other.fitted) 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) { for (const auto& pair : other.nodes) {
nodes[pair.first] = std::make_unique<Node>(*pair.second); nodes[pair.first] = std::make_unique<Node>(*pair.second);
} }
} }
@@ -20,7 +20,7 @@ namespace bayesnet {
{ {
return samples; return samples;
} }
void Network::addNode(string name, int numStates) void Network::addNode(const string& name, int numStates)
{ {
if (find(features.begin(), features.end(), name) == features.end()) { if (find(features.begin(), features.end(), name) == features.end()) {
features.push_back(name); features.push_back(name);
@@ -69,7 +69,7 @@ namespace bayesnet {
recStack.erase(nodeId); // remove node from recursion stack before function ends recStack.erase(nodeId); // remove node from recursion stack before function ends
return false; return false;
} }
void Network::addEdge(const string parent, const string child) void Network::addEdge(const string& parent, const string& child)
{ {
if (nodes.find(parent) == nodes.end()) { if (nodes.find(parent) == nodes.end()) {
throw invalid_argument("Parent node " + parent + " does not exist"); throw invalid_argument("Parent node " + parent + " does not exist");
@@ -105,8 +105,8 @@ namespace bayesnet {
for (int i = 0; i < featureNames.size(); ++i) { for (int i = 0; i < featureNames.size(); ++i) {
auto column = torch::flatten(X.index({ "...", i })); auto column = torch::flatten(X.index({ "...", i }));
auto k = vector<int>(); auto k = vector<int>();
for (auto i = 0; i < X.size(0); ++i) { for (auto z = 0; z < X.size(0); ++z) {
k.push_back(column[i].item<int>()); k.push_back(column[z].item<int>());
} }
dataset[featureNames[i]] = k; dataset[featureNames[i]] = k;
} }
@@ -145,9 +145,6 @@ namespace bayesnet {
while (nextNodeIndex < nodes.size()) { while (nextNodeIndex < nodes.size()) {
unique_lock<mutex> lock(mtx); unique_lock<mutex> lock(mtx);
cv.wait(lock, [&activeThreads, &maxThreadsRunning]() { return activeThreads < maxThreadsRunning; }); cv.wait(lock, [&activeThreads, &maxThreadsRunning]() { return activeThreads < maxThreadsRunning; });
if (nextNodeIndex >= nodes.size()) {
break; // No more work remaining
}
threads.emplace_back([this, &nextNodeIndex, &mtx, &cv, &activeThreads]() { threads.emplace_back([this, &nextNodeIndex, &mtx, &cv, &activeThreads]() {
while (true) { while (true) {
unique_lock<mutex> lock(mtx); unique_lock<mutex> lock(mtx);
@@ -262,9 +259,7 @@ namespace bayesnet {
// Normalize result // Normalize result
double sum = accumulate(result.begin(), result.end(), 0.0); double sum = accumulate(result.begin(), result.end(), 0.0);
for (double& value : result) { transform(result.begin(), result.end(), result.begin(), [sum](double& value) { return value / sum; });
value /= sum;
}
return result; return result;
} }
vector<string> Network::show() vector<string> Network::show()
@@ -280,7 +275,7 @@ namespace bayesnet {
} }
return result; return result;
} }
vector<string> Network::graph(string title) vector<string> Network::graph(const string& title)
{ {
auto output = vector<string>(); auto output = vector<string>();
auto prefix = "digraph BayesNet {\nlabel=<BayesNet "; auto prefix = "digraph BayesNet {\nlabel=<BayesNet ";

View File

@@ -27,13 +27,13 @@ namespace bayesnet {
void completeFit(); void completeFit();
public: public:
Network(); Network();
Network(float, int); explicit Network(float, int);
Network(float); explicit Network(float);
Network(Network&); explicit Network(Network&);
torch::Tensor& getSamples(); torch::Tensor& getSamples();
float getmaxThreads(); float getmaxThreads();
void addNode(string, int); void addNode(const string&, int);
void addEdge(const string, const string); void addEdge(const string&, const string&);
map<string, std::unique_ptr<Node>>& getNodes(); map<string, std::unique_ptr<Node>>& getNodes();
vector<string> getFeatures(); vector<string> getFeatures();
int getStates(); int getStates();
@@ -48,7 +48,7 @@ namespace bayesnet {
vector<vector<double>> predict_proba(const vector<vector<int>>&); vector<vector<double>> predict_proba(const vector<vector<int>>&);
double score(const vector<vector<int>>&, const vector<int>&); double score(const vector<vector<int>>&, const vector<int>&);
vector<string> show(); vector<string> show();
vector<string> graph(string title); // Returns a vector of strings representing the graph in graphviz format vector<string> graph(const string& title); // Returns a vector of strings representing the graph in graphviz format
inline string version() { return "0.1.0"; } inline string version() { return "0.1.0"; }
}; };
} }

View File

@@ -88,18 +88,15 @@ namespace bayesnet {
{ {
// Get dimensions of the CPT // Get dimensions of the CPT
dimensions.push_back(numStates); dimensions.push_back(numStates);
for (auto father : getParents()) { transform(parents.begin(), parents.end(), back_inserter(dimensions), [](const auto& parent) { return parent->getNumStates(); });
dimensions.push_back(father->getNumStates());
}
// Create a tensor of zeros with the dimensions of the CPT // Create a tensor of zeros with the dimensions of the CPT
cpTable = torch::zeros(dimensions, torch::kFloat) + laplaceSmoothing; cpTable = torch::zeros(dimensions, torch::kFloat) + laplaceSmoothing;
// Fill table with counts // Fill table with counts
for (int n_sample = 0; n_sample < dataset[name].size(); ++n_sample) { for (int n_sample = 0; n_sample < dataset[name].size(); ++n_sample) {
torch::List<c10::optional<torch::Tensor>> coordinates; torch::List<c10::optional<torch::Tensor>> coordinates;
coordinates.push_back(torch::tensor(dataset[name][n_sample])); coordinates.push_back(torch::tensor(dataset[name][n_sample]));
for (auto father : getParents()) { transform(parents.begin(), parents.end(), back_inserter(coordinates), [&dataset, &n_sample](const auto& parent) { return torch::tensor(dataset[parent->getName()][n_sample]); });
coordinates.push_back(torch::tensor(dataset[father->getName()][n_sample]));
}
// Increment the count of the corresponding coordinate // Increment the count of the corresponding coordinate
cpTable.index_put_({ coordinates }, cpTable.index({ coordinates }) + 1); cpTable.index_put_({ coordinates }, cpTable.index({ coordinates }) + 1);
} }
@@ -111,19 +108,15 @@ namespace bayesnet {
torch::List<c10::optional<torch::Tensor>> coordinates; torch::List<c10::optional<torch::Tensor>> coordinates;
// following predetermined order of indices in the cpTable (see Node.h) // following predetermined order of indices in the cpTable (see Node.h)
coordinates.push_back(torch::tensor(evidence[name])); coordinates.push_back(torch::tensor(evidence[name]));
for (auto parent : getParents()) { transform(parents.begin(), parents.end(), back_inserter(coordinates), [&evidence](const auto& parent) { return torch::tensor(evidence[parent->getName()]); });
coordinates.push_back(torch::tensor(evidence[parent->getName()]));
}
return cpTable.index({ coordinates }).item<float>(); return cpTable.index({ coordinates }).item<float>();
} }
vector<string> Node::graph(string className) vector<string> Node::graph(const string& className)
{ {
auto output = vector<string>(); auto output = vector<string>();
auto suffix = name == className ? ", fontcolor=red, fillcolor=lightblue, style=filled " : ""; auto suffix = name == className ? ", fontcolor=red, fillcolor=lightblue, style=filled " : "";
output.push_back(name + " [shape=circle" + suffix + "] \n"); output.push_back(name + " [shape=circle" + suffix + "] \n");
for (auto& child : children) { transform(children.begin(), children.end(), back_inserter(output), [this](const auto& child) { return name + " -> " + child->getName(); });
output.push_back(name + " -> " + child->getName());
}
return output; return output;
} }
} }

View File

@@ -16,7 +16,7 @@ namespace bayesnet {
vector<int64_t> dimensions; // dimensions of the cpTable vector<int64_t> dimensions; // dimensions of the cpTable
public: public:
vector<pair<string, string>> combinations(const vector<string>&); vector<pair<string, string>> combinations(const vector<string>&);
Node(const std::string&, int); Node(const string&, int);
void clear(); void clear();
void addParent(Node*); void addParent(Node*);
void addChild(Node*); void addChild(Node*);
@@ -30,7 +30,7 @@ namespace bayesnet {
int getNumStates() const; int getNumStates() const;
void setNumStates(int); void setNumStates(int);
unsigned minFill(); unsigned minFill();
vector<string> graph(string clasName); // Returns a vector of strings representing the graph in graphviz format vector<string> graph(const string& clasName); // Returns a vector of strings representing the graph in graphviz format
float getFactorValue(map<string, int>&); float getFactorValue(map<string, int>&);
}; };
} }

View File

@@ -9,7 +9,7 @@ namespace bayesnet {
protected: protected:
void train() override; void train() override;
public: public:
SPODE(int root); explicit SPODE(int root);
virtual ~SPODE() {}; virtual ~SPODE() {};
vector<string> graph(string name = "SPODE") override; vector<string> graph(string name = "SPODE") override;
}; };

View File

@@ -18,7 +18,7 @@ namespace bayesnet {
auto mi_value = metrics.mutualInformation(class_dataset, feature_dataset); auto mi_value = metrics.mutualInformation(class_dataset, feature_dataset);
mi.push_back({ i, mi_value }); mi.push_back({ i, mi_value });
} }
sort(mi.begin(), mi.end(), [](auto& left, auto& right) {return left.second < right.second;}); sort(mi.begin(), mi.end(), [](const auto& left, const auto& right) {return left.second < right.second;});
auto root = mi[mi.size() - 1].first; auto root = mi[mi.size() - 1].first;
// 2. Compute mutual information between each feature and the class // 2. Compute mutual information between each feature and the class
auto weights = metrics.conditionalEdge(); auto weights = metrics.conditionalEdge();

View File

@@ -4,9 +4,9 @@
namespace platform { namespace platform {
void Datasets::load() void Datasets::load()
{ {
string line;
ifstream catalog(path + "/all.txt"); ifstream catalog(path + "/all.txt");
if (catalog.is_open()) { if (catalog.is_open()) {
string line;
while (getline(catalog, line)) { while (getline(catalog, line)) {
vector<string> tokens = split(line, ','); vector<string> tokens = split(line, ',');
string name = tokens[0]; string name = tokens[0];
@@ -21,9 +21,7 @@ namespace platform {
vector<string> Datasets::getNames() vector<string> Datasets::getNames()
{ {
vector<string> result; vector<string> result;
for (auto& d : datasets) { transform(datasets.begin(), datasets.end(), back_inserter(result), [](const auto& d) { return d.first; });
result.push_back(d.first);
}
return result; return result;
} }
vector<string> Datasets::getFeatures(string name) vector<string> Datasets::getFeatures(string name)
@@ -79,27 +77,12 @@ namespace platform {
} }
return datasets[name]->getTensors(); return datasets[name]->getTensors();
} }
bool Datasets::isDataset(string name) bool Datasets::isDataset(const string& name)
{ {
return datasets.find(name) != datasets.end(); return datasets.find(name) != datasets.end();
} }
Dataset::Dataset(Dataset& dataset) Dataset::Dataset(const Dataset& dataset) : path(dataset.path), name(dataset.name), className(dataset.className), n_samples(dataset.n_samples), n_features(dataset.n_features), features(dataset.features), states(dataset.states), loaded(dataset.loaded), discretize(dataset.discretize), X(dataset.X), y(dataset.y), Xv(dataset.Xv), Xd(dataset.Xd), yv(dataset.yv), fileType(dataset.fileType)
{ {
path = dataset.path;
name = dataset.name;
className = dataset.className;
n_samples = dataset.n_samples;
n_features = dataset.n_features;
features = dataset.features;
states = dataset.states;
loaded = dataset.loaded;
discretize = dataset.discretize;
X = dataset.X;
y = dataset.y;
Xv = dataset.Xv;
Xd = dataset.Xd;
yv = dataset.yv;
fileType = dataset.fileType;
} }
string Dataset::getName() string Dataset::getName()
{ {
@@ -168,9 +151,9 @@ namespace platform {
} }
void Dataset::load_csv() void Dataset::load_csv()
{ {
string line;
ifstream file(path + "/" + name + ".csv"); ifstream file(path + "/" + name + ".csv");
if (file.is_open()) { if (file.is_open()) {
string line;
getline(file, line); getline(file, line);
vector<string> tokens = split(line, ','); vector<string> tokens = split(line, ',');
features = vector<string>(tokens.begin(), tokens.end() - 1); features = vector<string>(tokens.begin(), tokens.end() - 1);
@@ -208,9 +191,8 @@ namespace platform {
yv = arff.getY(); yv = arff.getY();
// Get className & Features // Get className & Features
className = arff.getClassName(); className = arff.getClassName();
for (auto feature : arff.getAttributes()) { auto attributes = arff.getAttributes();
features.push_back(feature.first); transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& attribute) { return attribute.first; });
}
} }
void Dataset::load() void Dataset::load()
{ {

View File

@@ -13,7 +13,7 @@ namespace platform {
string name; string name;
fileType_t fileType; fileType_t fileType;
string className; string className;
int n_samples, n_features; int n_samples{ 0 }, n_features{ 0 };
vector<string> features; vector<string> features;
map<string, vector<int>> states; map<string, vector<int>> states;
bool loaded; bool loaded;
@@ -27,8 +27,8 @@ namespace platform {
void load_arff(); void load_arff();
void computeStates(); void computeStates();
public: public:
Dataset(string path, string name, string className, bool discretize, fileType_t fileType) : path(path), name(name), className(className), discretize(discretize), loaded(false), fileType(fileType) {}; Dataset(const string& path, const string& name, const string& className, bool discretize, fileType_t fileType) : path(path), name(name), className(className), discretize(discretize), loaded(false), fileType(fileType) {};
Dataset(Dataset&); explicit Dataset(const Dataset&);
string getName(); string getName();
string getClassName(); string getClassName();
vector<string> getFeatures(); vector<string> getFeatures();
@@ -49,7 +49,7 @@ namespace platform {
bool discretize; bool discretize;
void load(); // Loads the list of datasets void load(); // Loads the list of datasets
public: public:
Datasets(string path, bool discretize = false, fileType_t fileType = ARFF) : path(path), discretize(discretize), fileType(fileType) { load(); }; explicit Datasets(const string& path, bool discretize = false, fileType_t fileType = ARFF) : path(path), discretize(discretize), fileType(fileType) { load(); };
vector<string> getNames(); vector<string> getNames();
vector<string> getFeatures(string name); vector<string> getFeatures(string name);
int getNSamples(string name); int getNSamples(string name);
@@ -58,7 +58,7 @@ namespace platform {
pair<vector<vector<float>>&, vector<int>&> getVectors(string name); pair<vector<vector<float>>&, vector<int>&> getVectors(string name);
pair<vector<vector<int>>&, vector<int>&> getVectorsDiscretized(string name); pair<vector<vector<int>>&, vector<int>&> getVectorsDiscretized(string name);
pair<torch::Tensor&, torch::Tensor&> getTensors(string name); pair<torch::Tensor&, torch::Tensor&> getTensors(string name);
bool isDataset(string name); bool isDataset(const string& name);
}; };
}; };

View File

@@ -52,9 +52,9 @@ namespace platform {
seeds_str = trim(seeds_str); seeds_str = trim(seeds_str);
seeds_str = seeds_str.substr(1, seeds_str.size() - 2); seeds_str = seeds_str.substr(1, seeds_str.size() - 2);
auto seeds_str_split = split(seeds_str, ','); auto seeds_str_split = split(seeds_str, ',');
for (auto seed_str : seeds_str_split) { transform(seeds_str_split.begin(), seeds_str_split.end(), back_inserter(seeds), [](const std::string& str) {
seeds.push_back(stoi(seed_str)); return stoi(str);
} });
return seeds; return seeds;
} }
}; };

View File

@@ -48,7 +48,7 @@ namespace platform {
result["seeds"] = randomSeeds; result["seeds"] = randomSeeds;
result["duration"] = duration; result["duration"] = duration;
result["results"] = json::array(); result["results"] = json::array();
for (auto& r : results) { for (const auto& r : results) {
json j; json j;
j["dataset"] = r.getDataset(); j["dataset"] = r.getDataset();
j["hyperparameters"] = r.getHyperparameters(); j["hyperparameters"] = r.getHyperparameters();
@@ -78,19 +78,31 @@ namespace platform {
} }
return result; return result;
} }
void Experiment::save(string path) void Experiment::save(const string& path)
{ {
json data = build_json(); json data = build_json();
ofstream file(path + "/" + get_file_name()); ofstream file(path + "/" + get_file_name());
file << data; file << data;
file.close(); file.close();
} }
void Experiment::show() void Experiment::show()
{ {
json data = build_json(); json data = build_json();
cout << data.dump(4) << endl; cout << data.dump(4) << endl;
} }
Result Experiment::cross_validation(const string& path, const string& fileName)
void Experiment::go(vector<string> filesToProcess, const string& path)
{
cout << "*** Starting experiment: " << title << " ***" << endl;
for (auto fileName : filesToProcess) {
cout << "- " << setw(20) << left << fileName << " " << right << flush;
cross_validation(path, fileName);
cout << endl;
}
}
void Experiment::cross_validation(const string& path, const string& fileName)
{ {
auto datasets = platform::Datasets(path, true, platform::ARFF); auto datasets = platform::Datasets(path, true, platform::ARFF);
// Get dataset // Get dataset
@@ -158,6 +170,7 @@ namespace platform {
result.setScoreTestStd(torch::std(accuracy_test).item<double>()).setScoreTrainStd(torch::std(accuracy_train).item<double>()); result.setScoreTestStd(torch::std(accuracy_test).item<double>()).setScoreTrainStd(torch::std(accuracy_train).item<double>());
result.setTrainTime(torch::mean(train_time).item<double>()).setTestTime(torch::mean(test_time).item<double>()); result.setTrainTime(torch::mean(train_time).item<double>()).setTestTime(torch::mean(test_time).item<double>());
result.setNodes(torch::mean(nodes).item<double>()).setLeaves(torch::mean(edges).item<double>()).setDepth(torch::mean(num_states).item<double>()); result.setNodes(torch::mean(nodes).item<double>()).setLeaves(torch::mean(edges).item<double>()).setDepth(torch::mean(num_states).item<double>());
return result; result.setDataset(fileName);
addResult(result);
} }
} }

View File

@@ -30,14 +30,14 @@ namespace platform {
class Result { class Result {
private: private:
string dataset, hyperparameters, model_version; string dataset, hyperparameters, model_version;
int samples, features, classes; int samples{ 0 }, features{ 0 }, classes{ 0 };
double score_train, score_test, score_train_std, score_test_std, train_time, train_time_std, test_time, test_time_std; double score_train{ 0 }, score_test{ 0 }, score_train_std{ 0 }, score_test_std{ 0 }, train_time{ 0 }, train_time_std{ 0 }, test_time{ 0 }, test_time_std{ 0 };
float nodes, leaves, depth; float nodes{ 0 }, leaves{ 0 }, depth{ 0 };
vector<double> scores_train, scores_test, times_train, times_test; vector<double> scores_train, scores_test, times_train, times_test;
public: public:
Result() = default; Result() = default;
Result& setDataset(string dataset) { this->dataset = dataset; return *this; } Result& setDataset(const string& dataset) { this->dataset = dataset; return *this; }
Result& setHyperparameters(string hyperparameters) { this->hyperparameters = hyperparameters; return *this; } Result& setHyperparameters(const string& hyperparameters) { this->hyperparameters = hyperparameters; return *this; }
Result& setSamples(int samples) { this->samples = samples; return *this; } Result& setSamples(int samples) { this->samples = samples; return *this; }
Result& setFeatures(int features) { this->features = features; return *this; } Result& setFeatures(int features) { this->features = features; return *this; }
Result& setClasses(int classes) { this->classes = classes; return *this; } Result& setClasses(int classes) { this->classes = classes; return *this; }
@@ -82,21 +82,21 @@ namespace platform {
class Experiment { class Experiment {
private: private:
string title, model, platform, score_name, model_version, language_version, language; string title, model, platform, score_name, model_version, language_version, language;
bool discretized, stratified; bool discretized{ false }, stratified{ false };
vector<Result> results; vector<Result> results;
vector<int> randomSeeds; vector<int> randomSeeds;
int nfolds; int nfolds{ 0 };
float duration; float duration{ 0 };
json build_json(); json build_json();
public: public:
Experiment() = default; Experiment() = default;
Experiment& setTitle(string title) { this->title = title; return *this; } Experiment& setTitle(const string& title) { this->title = title; return *this; }
Experiment& setModel(string model) { this->model = model; return *this; } Experiment& setModel(const string& model) { this->model = model; return *this; }
Experiment& setPlatform(string platform) { this->platform = platform; return *this; } Experiment& setPlatform(const string& platform) { this->platform = platform; return *this; }
Experiment& setScoreName(string score_name) { this->score_name = score_name; return *this; } Experiment& setScoreName(const string& score_name) { this->score_name = score_name; return *this; }
Experiment& setModelVersion(string model_version) { this->model_version = model_version; return *this; } Experiment& setModelVersion(const string& model_version) { this->model_version = model_version; return *this; }
Experiment& setLanguage(string language) { this->language = language; return *this; } Experiment& setLanguage(const string& language) { this->language = language; return *this; }
Experiment& setLanguageVersion(string language_version) { this->language_version = language_version; return *this; } Experiment& setLanguageVersion(const string& language_version) { this->language_version = language_version; return *this; }
Experiment& setDiscretized(bool discretized) { this->discretized = discretized; return *this; } Experiment& setDiscretized(bool discretized) { this->discretized = discretized; return *this; }
Experiment& setStratified(bool stratified) { this->stratified = stratified; return *this; } Experiment& setStratified(bool stratified) { this->stratified = stratified; return *this; }
Experiment& setNFolds(int nfolds) { this->nfolds = nfolds; return *this; } Experiment& setNFolds(int nfolds) { this->nfolds = nfolds; return *this; }
@@ -104,8 +104,9 @@ namespace platform {
Experiment& addRandomSeed(int randomSeed) { randomSeeds.push_back(randomSeed); return *this; } Experiment& addRandomSeed(int randomSeed) { randomSeeds.push_back(randomSeed); return *this; }
Experiment& setDuration(float duration) { this->duration = duration; return *this; } Experiment& setDuration(float duration) { this->duration = duration; return *this; }
string get_file_name(); string get_file_name();
void save(string path); void save(const string& path);
Result cross_validation(const string& path, const string& fileName); void cross_validation(const string& path, const string& fileName);
void go(vector<string> filesToProcess, const string& path);
void show(); void show();
}; };
} }

View File

@@ -7,9 +7,8 @@ Fold::Fold(int k, int n, int seed) : k(k), n(n), seed(seed)
random_seed = default_random_engine(seed == -1 ? rd() : seed); random_seed = default_random_engine(seed == -1 ? rd() : seed);
srand(seed == -1 ? time(0) : seed); srand(seed == -1 ? time(0) : seed);
} }
KFold::KFold(int k, int n, int seed) : Fold(k, n, seed) KFold::KFold(int k, int n, int seed) : Fold(k, n, seed), indices(vector<int>(n))
{ {
indices = vector<int>(n);
iota(begin(indices), end(indices), 0); // fill with 0, 1, ..., n - 1 iota(begin(indices), end(indices), 0); // fill with 0, 1, ..., n - 1
shuffle(indices.begin(), indices.end(), random_seed); shuffle(indices.begin(), indices.end(), random_seed);
} }

View File

@@ -22,7 +22,7 @@ private:
vector<int> indices; vector<int> indices;
public: public:
KFold(int k, int n, int seed = -1); KFold(int k, int n, int seed = -1);
pair<vector<int>, vector<int>> getFold(int nFold); pair<vector<int>, vector<int>> getFold(int nFold) override;
}; };
class StratifiedKFold : public Fold { class StratifiedKFold : public Fold {
private: private:
@@ -32,6 +32,6 @@ private:
public: public:
StratifiedKFold(int k, const vector<int>& y, int seed = -1); StratifiedKFold(int k, const vector<int>& y, int seed = -1);
StratifiedKFold(int k, torch::Tensor& y, int seed = -1); StratifiedKFold(int k, torch::Tensor& y, int seed = -1);
pair<vector<int>, vector<int>> getFold(int nFold); pair<vector<int>, vector<int>> getFold(int nFold) override;
}; };
#endif #endif

View File

@@ -2,25 +2,6 @@
namespace platform { namespace platform {
using namespace std; using namespace std;
// Idea from: https://www.codeproject.com/Articles/567242/AplusC-2b-2bplusObjectplusFactory // Idea from: https://www.codeproject.com/Articles/567242/AplusC-2b-2bplusObjectplusFactory
// shared_ptr<bayesnet::BaseClassifier> Models::createInstance(const string& name)
// {
// bayesnet::BaseClassifier* instance = nullptr;
// if (name == "AODE") {
// instance = new bayesnet::AODE();
// } else if (name == "KDB") {
// instance = new bayesnet::KDB(2);
// } else if (name == "SPODE") {
// instance = new bayesnet::SPODE(2);
// } else if (name == "TAN") {
// instance = new bayesnet::TAN();
// } else {
// throw runtime_error("Model " + name + " not found");
// }
// if (instance != nullptr)
// return shared_ptr<bayesnet::BaseClassifier>(instance);
// else
// return nullptr;
// }
Models* Models::factory = nullptr;; Models* Models::factory = nullptr;;
Models* Models::instance() Models* Models::instance()
{ {
@@ -59,7 +40,7 @@ namespace platform {
string Models::toString() string Models::toString()
{ {
string result = ""; string result = "";
for (auto& pair : functionRegistry) { for (const auto& pair : functionRegistry) {
result += pair.first + ", "; result += pair.first + ", ";
} }
return "{" + result.substr(0, result.size() - 2) + "}"; return "{" + result.substr(0, result.size() - 2) + "}";

View File

@@ -5,7 +5,7 @@
#include "Datasets.h" #include "Datasets.h"
#include "DotEnv.h" #include "DotEnv.h"
#include "Models.h" #include "Models.h"
#include "modelRegister.h"
using namespace std; using namespace std;
const string PATH_RESULTS = "results"; const string PATH_RESULTS = "results";
@@ -49,22 +49,17 @@ argparse::ArgumentParser manageArguments(int argc, char** argv)
}}); }});
auto seed_values = env.getSeeds(); auto seed_values = env.getSeeds();
program.add_argument("-s", "--seeds").nargs(1, 10).help("Random seeds. Set to -1 to have pseudo random").scan<'i', int>().default_value(seed_values); program.add_argument("-s", "--seeds").nargs(1, 10).help("Random seeds. Set to -1 to have pseudo random").scan<'i', int>().default_value(seed_values);
bool class_last, discretize_dataset, stratified;
int n_folds;
vector<int> seeds;
string model_name, file_name, path, complete_file_name, title;
try { try {
program.parse_args(argc, argv); program.parse_args(argc, argv);
file_name = program.get<string>("dataset"); auto file_name = program.get<string>("dataset");
path = program.get<string>("path"); auto path = program.get<string>("path");
model_name = program.get<string>("model"); auto model_name = program.get<string>("model");
discretize_dataset = program.get<bool>("discretize"); auto discretize_dataset = program.get<bool>("discretize");
stratified = program.get<bool>("stratified"); auto stratified = program.get<bool>("stratified");
n_folds = program.get<int>("folds"); auto n_folds = program.get<int>("folds");
seeds = program.get<vector<int>>("seeds"); auto seeds = program.get<vector<int>>("seeds");
complete_file_name = path + file_name + ".arff"; auto complete_file_name = path + file_name + ".arff";
class_last = false;//datasets[file_name]; auto title = program.get<string>("title");
title = program.get<string>("title");
if (title == "" && file_name == "") { if (title == "" && file_name == "") {
throw runtime_error("title is mandatory if dataset is not provided"); throw runtime_error("title is mandatory if dataset is not provided");
} }
@@ -76,21 +71,9 @@ argparse::ArgumentParser manageArguments(int argc, char** argv)
} }
return program; return program;
} }
void registerModels()
{
static platform::Registrar registrarT("TAN",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::TAN();});
static platform::Registrar registrarS("SPODE",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::SPODE(2);});
static platform::Registrar registrarK("KDB",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::KDB(2);});
static platform::Registrar registrarA("AODE",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::AODE();});
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
registerModels();
auto program = manageArguments(argc, argv); auto program = manageArguments(argc, argv);
bool saveResults = false; bool saveResults = false;
auto file_name = program.get<string>("dataset"); auto file_name = program.get<string>("dataset");
@@ -100,7 +83,7 @@ int main(int argc, char** argv)
auto stratified = program.get<bool>("stratified"); auto stratified = program.get<bool>("stratified");
auto n_folds = program.get<int>("folds"); auto n_folds = program.get<int>("folds");
auto seeds = program.get<vector<int>>("seeds"); auto seeds = program.get<vector<int>>("seeds");
vector<string> filesToProcess; vector<string> filesToTest;
auto datasets = platform::Datasets(path, true, platform::ARFF); auto datasets = platform::Datasets(path, true, platform::ARFF);
auto title = program.get<string>("title"); auto title = program.get<string>("title");
if (file_name != "") { if (file_name != "") {
@@ -111,9 +94,9 @@ int main(int argc, char** argv)
if (title == "") { if (title == "") {
title = "Test " + file_name + " " + model_name + " " + to_string(n_folds) + " folds"; title = "Test " + file_name + " " + model_name + " " + to_string(n_folds) + " folds";
} }
filesToProcess.push_back(file_name); filesToTest.push_back(file_name);
} else { } else {
filesToProcess = platform::Datasets(path, true, platform::ARFF).getNames(); filesToTest = platform::Datasets(path, true, platform::ARFF).getNames();
saveResults = true; saveResults = true;
} }
@@ -128,15 +111,8 @@ int main(int argc, char** argv)
experiment.addRandomSeed(seed); experiment.addRandomSeed(seed);
} }
platform::Timer timer; platform::Timer timer;
cout << "*** Starting experiment: " << title << " ***" << endl;
timer.start(); timer.start();
for (auto fileName : filesToProcess) { experiment.go(filesToTest, path);
cout << "- " << setw(20) << left << fileName << " " << right << flush;
auto result = experiment.cross_validation(path, fileName);
result.setDataset(fileName);
experiment.addResult(result);
cout << endl;
}
experiment.setDuration(timer.getDuration()); experiment.setDuration(timer.getDuration());
if (saveResults) if (saveResults)
experiment.save(PATH_RESULTS); experiment.save(PATH_RESULTS);

View File

@@ -0,0 +1,11 @@
#ifndef MODEL_REGISTER_H
#define MODEL_REGISTER_H
static platform::Registrar registrarT("TAN",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::TAN();});
static platform::Registrar registrarS("SPODE",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::SPODE(2);});
static platform::Registrar registrarK("KDB",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::KDB(2);});
static platform::Registrar registrarA("AODE",
[](void) -> bayesnet::BaseClassifier* { return new bayesnet::AODE();});
#endif

View File

@@ -2,7 +2,7 @@
using namespace torch; using namespace torch;
vector<string> split(string text, char delimiter) vector<string> split(const string& text, char delimiter)
{ {
vector<string> result; vector<string> result;
stringstream ss(text); stringstream ss(text);
@@ -39,7 +39,7 @@ vector<mdlp::labels_t> discretizeDataset(vector<mdlp::samples_t>& X, mdlp::label
return Xd; return Xd;
} }
bool file_exists(const std::string& name) bool file_exists(const string& name)
{ {
if (FILE* file = fopen(name.c_str(), "r")) { if (FILE* file = fopen(name.c_str(), "r")) {
fclose(file); fclose(file);
@@ -49,7 +49,7 @@ bool file_exists(const std::string& name)
} }
} }
tuple<Tensor, Tensor, vector<string>, string, map<string, vector<int>>> loadDataset(string path, string name, bool class_last, bool discretize_dataset) tuple<Tensor, Tensor, vector<string>, string, map<string, vector<int>>> loadDataset(const string& path, const string& name, bool class_last, bool discretize_dataset)
{ {
auto handler = ArffFiles(); auto handler = ArffFiles();
handler.load(path + static_cast<string>(name) + ".arff", class_last); handler.load(path + static_cast<string>(name) + ".arff", class_last);
@@ -59,9 +59,8 @@ tuple<Tensor, Tensor, vector<string>, string, map<string, vector<int>>> loadData
// Get className & Features // Get className & Features
auto className = handler.getClassName(); auto className = handler.getClassName();
vector<string> features; vector<string> features;
for (auto feature : handler.getAttributes()) { auto attributes = handler.getAttributes();
features.push_back(feature.first); transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& pair) { return pair.first; });
}
Tensor Xd; Tensor Xd;
auto states = map<string, vector<int>>(); auto states = map<string, vector<int>>();
if (discretize_dataset) { if (discretize_dataset) {
@@ -83,7 +82,7 @@ tuple<Tensor, Tensor, vector<string>, string, map<string, vector<int>>> loadData
return { Xd, torch::tensor(y, torch::kInt32), features, className, states }; return { Xd, torch::tensor(y, torch::kInt32), features, className, states };
} }
tuple<vector<vector<int>>, vector<int>, vector<string>, string, map<string, vector<int>>> loadFile(string name) tuple<vector<vector<int>>, vector<int>, vector<string>, string, map<string, vector<int>>> loadFile(const string& name)
{ {
auto handler = ArffFiles(); auto handler = ArffFiles();
handler.load(PATH + static_cast<string>(name) + ".arff"); handler.load(PATH + static_cast<string>(name) + ".arff");
@@ -93,9 +92,8 @@ tuple<vector<vector<int>>, vector<int>, vector<string>, string, map<string, vect
// Get className & Features // Get className & Features
auto className = handler.getClassName(); auto className = handler.getClassName();
vector<string> features; vector<string> features;
for (auto feature : handler.getAttributes()) { auto attributes = handler.getAttributes();
features.push_back(feature.first); transform(attributes.begin(), attributes.end(), back_inserter(features), [](const auto& pair) { return pair.first; });
}
// Discretize Dataset // Discretize Dataset
vector<mdlp::labels_t> Xd; vector<mdlp::labels_t> Xd;
map<string, int> maxes; map<string, int> maxes;

View File

@@ -11,11 +11,11 @@ using namespace std;
const string PATH = "../../data/"; const string PATH = "../../data/";
bool file_exists(const std::string& name); bool file_exists(const std::string& name);
vector<string> split(string text, char delimiter); vector<string> split(const string& text, char delimiter);
pair<vector<mdlp::labels_t>, map<string, int>> discretize(vector<mdlp::samples_t>& X, mdlp::labels_t& y, vector<string> features); pair<vector<mdlp::labels_t>, map<string, int>> discretize(vector<mdlp::samples_t>& X, mdlp::labels_t& y, vector<string> features);
vector<mdlp::labels_t> discretizeDataset(vector<mdlp::samples_t>& X, mdlp::labels_t& y); vector<mdlp::labels_t> discretizeDataset(vector<mdlp::samples_t>& X, mdlp::labels_t& y);
pair<torch::Tensor, map<string, vector<int>>> discretizeTorch(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className); pair<torch::Tensor, map<string, vector<int>>> discretizeTorch(torch::Tensor& X, torch::Tensor& y, vector<string>& features, const string& className);
tuple<vector<vector<int>>, vector<int>, vector<string>, string, map<string, vector<int>>> loadFile(string name); tuple<vector<vector<int>>, vector<int>, vector<string>, string, map<string, vector<int>>> loadFile(const string& name);
tuple<torch::Tensor, torch::Tensor, vector<string>, string, map<string, vector<int>>> loadDataset(string path, string name, bool class_last, bool discretize_dataset); tuple<torch::Tensor, torch::Tensor, vector<string>, string, map<string, vector<int>>> loadDataset(const string& path, const string& name, bool class_last, bool discretize_dataset);
map<string, vector<int>> get_states(vector<string>& features, string className, map<string, int>& maxes); map<string, vector<int>> get_states(vector<string>& features, string className, map<string, int>& maxes);
#endif //PLATFORM_UTILS_H #endif //PLATFORM_UTILS_H