Compare commits

...

6 Commits

14 changed files with 319 additions and 88 deletions

View File

@@ -8,6 +8,7 @@ namespace bayesnet {
void train() override; void train() override;
public: public:
AODE(); AODE();
virtual ~AODE() {};
vector<string> graph(string title = "AODE") override; vector<string> graph(string title = "AODE") override;
}; };
} }

View File

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

View File

@@ -1,6 +1,7 @@
#ifndef SPODE_H #ifndef SPODE_H
#define SPODE_H #define SPODE_H
#include "Classifier.h" #include "Classifier.h"
namespace bayesnet { namespace bayesnet {
class SPODE : public Classifier { class SPODE : public Classifier {
private: private:
@@ -9,6 +10,7 @@ namespace bayesnet {
void train() override; void train() override;
public: public:
SPODE(int root); SPODE(int root);
virtual ~SPODE() {};
vector<string> graph(string name = "SPODE") override; vector<string> graph(string name = "SPODE") override;
}; };
} }

View File

@@ -10,6 +10,7 @@ namespace bayesnet {
void train() override; void train() override;
public: public:
TAN(); TAN();
virtual ~TAN() {};
vector<string> graph(string name = "TAN") override; vector<string> graph(string name = "TAN") override;
}; };
} }

View File

@@ -4,5 +4,5 @@ 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)
include_directories(${BayesNet_SOURCE_DIR}/lib/json/include) include_directories(${BayesNet_SOURCE_DIR}/lib/json/include)
add_executable(main main.cc Folding.cc platformUtils.cc Experiment.cc Datasets.cc) add_executable(main main.cc Folding.cc platformUtils.cc Experiment.cc Datasets.cc Models.cc)
target_link_libraries(main BayesNet ArffFiles mdlp "${TORCH_LIBRARIES}") target_link_libraries(main BayesNet ArffFiles mdlp "${TORCH_LIBRARIES}")

View File

@@ -2,16 +2,6 @@
#include "platformUtils.h" #include "platformUtils.h"
#include "ArffFiles.h" #include "ArffFiles.h"
namespace platform { namespace platform {
vector<string> split(string text, char delimiter)
{
vector<string> result;
stringstream ss(text);
string token;
while (getline(ss, token, delimiter)) {
result.push_back(token);
}
return result;
}
void Datasets::load() void Datasets::load()
{ {
string line; string line;

62
src/Platform/DotEnv.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef DOTENV_H
#define DOTENV_H
#include <string>
#include <map>
#include <fstream>
#include <sstream>
#include "platformUtils.h"
namespace platform {
class DotEnv {
private:
std::map<std::string, std::string> env;
std::string trim(const std::string& str)
{
std::string result = str;
result.erase(result.begin(), std::find_if(result.begin(), result.end(), [](int ch) {
return !std::isspace(ch);
}));
result.erase(std::find_if(result.rbegin(), result.rend(), [](int ch) {
return !std::isspace(ch);
}).base(), result.end());
return result;
}
public:
DotEnv()
{
std::ifstream file(".env");
if (!file.is_open()) {
std::cerr << "File .env not found" << std::endl;
exit(1);
}
std::string line;
while (std::getline(file, line)) {
line = trim(line);
if (line.empty() || line[0] == '#') {
continue;
}
std::istringstream iss(line);
std::string key, value;
if (std::getline(iss, key, '=') && std::getline(iss, value)) {
env[key] = value;
}
}
}
std::string get(const std::string& key)
{
return env[key];
}
std::vector<int> getSeeds()
{
auto seeds = std::vector<int>();
auto seeds_str = env["seeds"];
seeds_str = trim(seeds_str);
seeds_str = seeds_str.substr(1, seeds_str.size() - 2);
auto seeds_str_split = split(seeds_str, ',');
for (auto seed_str : seeds_str_split) {
seeds.push_back(stoi(seed_str));
}
return seeds;
}
};
}
#endif

View File

@@ -1,4 +1,6 @@
#include "Experiment.h" #include "Experiment.h"
#include "Datasets.h"
#include "Models.h"
namespace platform { namespace platform {
using json = nlohmann::json; using json = nlohmann::json;
@@ -43,7 +45,7 @@ namespace platform {
result["discretized"] = discretized; result["discretized"] = discretized;
result["stratified"] = stratified; result["stratified"] = stratified;
result["folds"] = nfolds; result["folds"] = nfolds;
result["seeds"] = random_seeds; result["seeds"] = randomSeeds;
result["duration"] = duration; result["duration"] = duration;
result["results"] = json::array(); result["results"] = json::array();
for (auto& r : results) { for (auto& r : results) {
@@ -65,6 +67,10 @@ namespace platform {
j["test_time_std"] = r.getTestTimeStd(); j["test_time_std"] = r.getTestTimeStd();
j["time"] = r.getTestTime() + r.getTrainTime(); j["time"] = r.getTestTime() + r.getTrainTime();
j["time_std"] = r.getTestTimeStd() + r.getTrainTimeStd(); j["time_std"] = r.getTestTimeStd() + r.getTrainTimeStd();
j["scores_train"] = r.getScoresTrain();
j["scores_test"] = r.getScoresTest();
j["times_train"] = r.getTimesTrain();
j["times_test"] = r.getTimesTest();
j["nodes"] = r.getNodes(); j["nodes"] = r.getNodes();
j["leaves"] = r.getLeaves(); j["leaves"] = r.getLeaves();
j["depth"] = r.getDepth(); j["depth"] = r.getDepth();
@@ -79,51 +85,75 @@ namespace platform {
file << data; file << data;
file.close(); file.close();
} }
Result cross_validation(Fold* fold, string model_name, torch::Tensor& Xt, torch::Tensor& y, vector<string> features, string className, map<string, vector<int>> states) void Experiment::show()
{ {
auto classifiers = map<string, bayesnet::BaseClassifier*>({ json data = build_json();
{ "AODE", new bayesnet::AODE() }, { "KDB", new bayesnet::KDB(2) }, cout << data.dump(4) << endl;
{ "SPODE", new bayesnet::SPODE(2) }, { "TAN", new bayesnet::TAN() }
} }
); Result Experiment::cross_validation(const string& path, const string& fileName)
{
auto datasets = platform::Datasets(path, true, platform::ARFF);
// Get dataset
auto [X, y] = datasets.getTensors(fileName);
auto states = datasets.getStates(fileName);
auto features = datasets.getFeatures(fileName);
auto samples = datasets.getNSamples(fileName);
auto className = datasets.getClassName(fileName);
cout << " (" << setw(5) << samples << "," << setw(3) << features.size() << ") " << flush;
// Prepare Result
auto result = Result(); auto result = Result();
auto [values, counts] = at::_unique(y); auto [values, counts] = at::_unique(y);;
result.setSamples(Xt.size(1)).setFeatures(Xt.size(0)).setClasses(values.size(0)); result.setSamples(X.size(1)).setFeatures(X.size(0)).setClasses(values.size(0));
auto k = fold->getNumberOfFolds(); int nResults = nfolds * static_cast<int>(randomSeeds.size());
auto accuracy_test = torch::zeros({ k }, torch::kFloat64); auto accuracy_test = torch::zeros({ nResults }, torch::kFloat64);
auto accuracy_train = torch::zeros({ k }, torch::kFloat64); auto accuracy_train = torch::zeros({ nResults }, torch::kFloat64);
auto train_time = torch::zeros({ k }, torch::kFloat64); auto train_time = torch::zeros({ nResults }, torch::kFloat64);
auto test_time = torch::zeros({ k }, torch::kFloat64); auto test_time = torch::zeros({ nResults }, torch::kFloat64);
auto nodes = torch::zeros({ k }, torch::kFloat64); auto nodes = torch::zeros({ nResults }, torch::kFloat64);
auto edges = torch::zeros({ k }, torch::kFloat64); auto edges = torch::zeros({ nResults }, torch::kFloat64);
auto num_states = torch::zeros({ k }, torch::kFloat64); auto num_states = torch::zeros({ nResults }, torch::kFloat64);
Timer train_timer, test_timer; Timer train_timer, test_timer;
cout << "doing Fold: " << flush; int item = 0;
for (int i = 0; i < k; i++) { for (auto seed : randomSeeds) {
bayesnet::BaseClassifier* model = classifiers[model_name]; cout << "(" << seed << ") doing Fold: " << flush;
result.setModelVersion(model->getVersion()); Fold* fold;
if (stratified)
fold = new StratifiedKFold(nfolds, y, seed);
else
fold = new KFold(nfolds, y.size(0), seed);
for (int nfold = 0; nfold < nfolds; nfold++) {
auto clf = Models::instance()->create(model);
setModelVersion(clf->getVersion());
train_timer.start(); train_timer.start();
auto [train, test] = fold->getFold(i); auto [train, test] = fold->getFold(nfold);
auto train_t = torch::tensor(train); auto train_t = torch::tensor(train);
auto test_t = torch::tensor(test); auto test_t = torch::tensor(test);
auto X_train = Xt.index({ "...", train_t }); auto X_train = X.index({ "...", train_t });
auto y_train = y.index({ train_t }); auto y_train = y.index({ train_t });
auto X_test = Xt.index({ "...", test_t }); auto X_test = X.index({ "...", test_t });
auto y_test = y.index({ test_t }); auto y_test = y.index({ test_t });
cout << i + 1 << ", " << flush; cout << nfold + 1 << ", " << flush;
model->fit(X_train, y_train, features, className, states); clf->fit(X_train, y_train, features, className, states);
nodes[i] = model->getNumberOfNodes(); nodes[item] = clf->getNumberOfNodes();
edges[i] = model->getNumberOfEdges(); edges[item] = clf->getNumberOfEdges();
num_states[i] = model->getNumberOfStates(); num_states[item] = clf->getNumberOfStates();
train_time[i] = train_timer.getDuration(); train_time[item] = train_timer.getDuration();
auto accuracy_train_value = model->score(X_train, y_train); auto accuracy_train_value = clf->score(X_train, y_train);
test_timer.start(); test_timer.start();
auto accuracy_test_value = model->score(X_test, y_test); auto accuracy_test_value = clf->score(X_test, y_test);
test_time[i] = test_timer.getDuration(); test_time[item] = test_timer.getDuration();
accuracy_train[i] = accuracy_train_value; accuracy_train[item] = accuracy_train_value;
accuracy_test[i] = accuracy_test_value; accuracy_test[item] = accuracy_test_value;
// Store results and times in vector
result.addScoreTrain(accuracy_train_value);
result.addScoreTest(accuracy_test_value);
result.addTimeTrain(train_time[item].item<double>());
result.addTimeTest(test_time[item].item<double>());
item++;
}
cout << "end. " << flush;
delete fold;
} }
cout << "end." << endl;
result.setScoreTest(torch::mean(accuracy_test).item<double>()).setScoreTrain(torch::mean(accuracy_train).item<double>()); result.setScoreTest(torch::mean(accuracy_test).item<double>()).setScoreTrain(torch::mean(accuracy_train).item<double>());
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>());

View File

@@ -33,6 +33,7 @@ namespace platform {
int samples, features, classes; int samples, features, classes;
double score_train, score_test, score_train_std, score_test_std, train_time, train_time_std, test_time, test_time_std; double score_train, score_test, score_train_std, score_test_std, train_time, train_time_std, test_time, test_time_std;
float nodes, leaves, depth; float nodes, leaves, depth;
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(string dataset) { this->dataset = dataset; return *this; }
@@ -51,7 +52,10 @@ namespace platform {
Result& setNodes(float nodes) { this->nodes = nodes; return *this; } Result& setNodes(float nodes) { this->nodes = nodes; return *this; }
Result& setLeaves(float leaves) { this->leaves = leaves; return *this; } Result& setLeaves(float leaves) { this->leaves = leaves; return *this; }
Result& setDepth(float depth) { this->depth = depth; return *this; } Result& setDepth(float depth) { this->depth = depth; return *this; }
Result& setModelVersion(string model_version) { this->model_version = model_version; return *this; } Result& addScoreTrain(double score) { scores_train.push_back(score); return *this; }
Result& addScoreTest(double score) { scores_test.push_back(score); return *this; }
Result& addTimeTrain(double time) { times_train.push_back(time); return *this; }
Result& addTimeTest(double time) { times_test.push_back(time); return *this; }
const float get_score_train() const { return score_train; } const float get_score_train() const { return score_train; }
float get_score_test() { return score_test; } float get_score_test() { return score_test; }
const string& getDataset() const { return dataset; } const string& getDataset() const { return dataset; }
@@ -70,14 +74,17 @@ namespace platform {
const float getNodes() const { return nodes; } const float getNodes() const { return nodes; }
const float getLeaves() const { return leaves; } const float getLeaves() const { return leaves; }
const float getDepth() const { return depth; } const float getDepth() const { return depth; }
const string& getModelVersion() const { return model_version; } const vector<double>& getScoresTrain() const { return scores_train; }
const vector<double>& getScoresTest() const { return scores_test; }
const vector<double>& getTimesTrain() const { return times_train; }
const vector<double>& getTimesTest() const { return times_test; }
}; };
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, stratified;
vector<Result> results; vector<Result> results;
vector<int> random_seeds; vector<int> randomSeeds;
int nfolds; int nfolds;
float duration; float duration;
json build_json(); json build_json();
@@ -94,12 +101,12 @@ namespace platform {
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; }
Experiment& addResult(Result result) { results.push_back(result); return *this; } Experiment& addResult(Result result) { results.push_back(result); return *this; }
Experiment& addRandomSeed(int random_seed) { random_seeds.push_back(random_seed); 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(string path);
void show() { cout << "Showing experiment..." << "Score Test: " << results[0].get_score_test() << " Score Train: " << results[0].get_score_train() << endl; } Result cross_validation(const string& path, const string& fileName);
void show();
}; };
Result cross_validation(Fold* fold, string model_name, torch::Tensor& X, torch::Tensor& y, vector<string> features, string className, map<string, vector<int>> states);
} }
#endif #endif

73
src/Platform/Models.cc Normal file
View File

@@ -0,0 +1,73 @@
#include "Models.h"
namespace platform {
using namespace std;
// 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::instance()
{
//manages singleton
if (factory == nullptr)
factory = new Models();
return factory;
}
void Models::registerFactoryFunction(const string& name,
function<bayesnet::BaseClassifier* (void)> classFactoryFunction)
{
// register the class factory function
functionRegistry[name] = classFactoryFunction;
}
shared_ptr<bayesnet::BaseClassifier> Models::create(const string& name)
{
bayesnet::BaseClassifier* instance = nullptr;
// find name in the registry and call factory method.
auto it = functionRegistry.find(name);
if (it != functionRegistry.end())
instance = it->second();
// wrap instance in a shared ptr and return
if (instance != nullptr)
return shared_ptr<bayesnet::BaseClassifier>(instance);
else
return nullptr;
}
vector<string> Models::getNames()
{
vector<string> names;
transform(functionRegistry.begin(), functionRegistry.end(), back_inserter(names),
[](const pair<string, function<bayesnet::BaseClassifier* (void)>>& pair) { return pair.first; });
return names;
}
string Models::toString()
{
string result = "";
for (auto& pair : functionRegistry) {
result += pair.first + ", ";
}
return "{" + result.substr(0, result.size() - 2) + "}";
}
Registrar::Registrar(const string& name, function<bayesnet::BaseClassifier* (void)> classFactoryFunction)
{
// register the class factory function
Models::instance()->registerFactoryFunction(name, classFactoryFunction);
}
}

32
src/Platform/Models.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef MODELS_H
#define MODELS_H
#include <map>
#include "BaseClassifier.h"
#include "AODE.h"
#include "TAN.h"
#include "KDB.h"
#include "SPODE.h"
namespace platform {
class Models {
private:
map<string, function<bayesnet::BaseClassifier* (void)>> functionRegistry;
static Models* factory; //singleton
Models() {};
public:
Models(Models&) = delete;
void operator=(const Models&) = delete;
// Idea from: https://www.codeproject.com/Articles/567242/AplusC-2b-2bplusObjectplusFactory
static Models* instance();
shared_ptr<bayesnet::BaseClassifier> create(const string& name);
void registerFactoryFunction(const string& name,
function<bayesnet::BaseClassifier* (void)> classFactoryFunction);
vector<string> getNames();
string toString();
};
class Registrar {
public:
Registrar(const string& className, function<bayesnet::BaseClassifier* (void)> classFactoryFunction);
};
}
#endif

View File

@@ -3,33 +3,37 @@
#include "platformUtils.h" #include "platformUtils.h"
#include "Experiment.h" #include "Experiment.h"
#include "Datasets.h" #include "Datasets.h"
#include "DotEnv.h"
#include "Models.h"
using namespace std; using namespace std;
const string PATH_RESULTS = "results"; const string PATH_RESULTS = "results";
const string PATH_DATASETS = "datasets";
argparse::ArgumentParser manageArguments(int argc, char** argv) argparse::ArgumentParser manageArguments(int argc, char** argv)
{ {
auto env = platform::DotEnv();
argparse::ArgumentParser program("BayesNetSample"); argparse::ArgumentParser program("BayesNetSample");
program.add_argument("-d", "--dataset").default_value("").help("Dataset file name"); program.add_argument("-d", "--dataset").default_value("").help("Dataset file name");
program.add_argument("-p", "--path") program.add_argument("-p", "--path")
.help("folder where the data files are located, default") .help("folder where the data files are located, default")
.default_value(string{ PATH } .default_value(string{ PATH_DATASETS }
); );
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("--title").required().help("Experiment title"); program.add_argument("--title").default_value("").help("Experiment title");
program.add_argument("--discretize").help("Discretize input dataset").default_value(false).implicit_value(true); program.add_argument("--discretize").help("Discretize input dataset").default_value((bool)stoi(env.get("discretize"))).implicit_value(true);
program.add_argument("--stratified").help("If Stratified KFold is to be done").default_value(false).implicit_value(true); program.add_argument("--stratified").help("If Stratified KFold is to be done").default_value((bool)stoi(env.get("stratified"))).implicit_value(true);
program.add_argument("-f", "--folds").help("Number of folds").default_value(5).scan<'i', int>().action([](const string& value) { program.add_argument("-f", "--folds").help("Number of folds").default_value(stoi(env.get("n_folds"))).scan<'i', int>().action([](const string& value) {
try { try {
auto k = stoi(value); auto k = stoi(value);
if (k < 2) { if (k < 2) {
@@ -43,9 +47,11 @@ argparse::ArgumentParser manageArguments(int argc, char** argv)
catch (...) { catch (...) {
throw runtime_error("Number of folds must be an integer"); throw runtime_error("Number of folds must be an integer");
}}); }});
program.add_argument("-s", "--seed").help("Random seed").default_value(-1).scan<'i', int>(); 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);
bool class_last, discretize_dataset, stratified; bool class_last, discretize_dataset, stratified;
int n_folds, seed; int n_folds;
vector<int> seeds;
string model_name, file_name, path, complete_file_name, title; string model_name, file_name, path, complete_file_name, title;
try { try {
program.parse_args(argc, argv); program.parse_args(argc, argv);
@@ -55,10 +61,13 @@ argparse::ArgumentParser manageArguments(int argc, char** argv)
discretize_dataset = program.get<bool>("discretize"); discretize_dataset = program.get<bool>("discretize");
stratified = program.get<bool>("stratified"); stratified = program.get<bool>("stratified");
n_folds = program.get<int>("folds"); n_folds = program.get<int>("folds");
seed = program.get<int>("seed"); seeds = program.get<vector<int>>("seeds");
complete_file_name = path + file_name + ".arff"; complete_file_name = path + file_name + ".arff";
class_last = false;//datasets[file_name]; class_last = false;//datasets[file_name];
title = program.get<string>("title"); title = program.get<string>("title");
if (title == "" && file_name == "") {
throw runtime_error("title is mandatory if dataset is not provided");
}
} }
catch (const exception& err) { catch (const exception& err) {
cerr << err.what() << endl; cerr << err.what() << endl;
@@ -67,29 +76,46 @@ 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;
auto file_name = program.get<string>("dataset"); auto file_name = program.get<string>("dataset");
auto path = program.get<string>("path"); auto path = program.get<string>("path");
auto model_name = program.get<string>("model"); auto model_name = program.get<string>("model");
auto discretize_dataset = program.get<bool>("discretize"); auto discretize_dataset = program.get<bool>("discretize");
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 seed = program.get<int>("seed"); auto seeds = program.get<vector<int>>("seeds");
vector<string> filesToProcess; vector<string> filesToProcess;
auto datasets = platform::Datasets(path, true, platform::ARFF); auto datasets = platform::Datasets(path, true, platform::ARFF);
auto title = program.get<string>("title");
if (file_name != "") { if (file_name != "") {
if (!datasets.isDataset(file_name)) { if (!datasets.isDataset(file_name)) {
cerr << "Dataset " << file_name << " not found" << endl; cerr << "Dataset " << file_name << " not found" << endl;
exit(1); exit(1);
} }
if (title == "") {
title = "Test " + file_name + " " + model_name + " " + to_string(n_folds) + " folds";
}
filesToProcess.push_back(file_name); filesToProcess.push_back(file_name);
} else { } else {
filesToProcess = platform::Datasets(path, true, platform::ARFF).getNames(); filesToProcess = platform::Datasets(path, true, platform::ARFF).getNames();
saveResults = true;
} }
auto title = program.get<string>("title");
/* /*
* Begin Processing * Begin Processing
@@ -97,31 +123,25 @@ int main(int argc, char** argv)
auto experiment = platform::Experiment(); auto experiment = platform::Experiment();
experiment.setTitle(title).setLanguage("cpp").setLanguageVersion("1.0.0"); experiment.setTitle(title).setLanguage("cpp").setLanguageVersion("1.0.0");
experiment.setDiscretized(discretize_dataset).setModel(model_name).setPlatform("BayesNet"); experiment.setDiscretized(discretize_dataset).setModel(model_name).setPlatform("BayesNet");
experiment.setStratified(stratified).setNFolds(n_folds).addRandomSeed(seed).setScoreName("accuracy"); experiment.setStratified(stratified).setNFolds(n_folds).setScoreName("accuracy");
for (auto seed : seeds) {
experiment.addRandomSeed(seed);
}
platform::Timer timer; platform::Timer timer;
cout << "*** Starting experiment: " << title << " ***" << endl; cout << "*** Starting experiment: " << title << " ***" << endl;
timer.start(); timer.start();
for (auto fileName : filesToProcess) { for (auto fileName : filesToProcess) {
cout << "- " << setw(20) << left << fileName << " " << right << flush; cout << "- " << setw(20) << left << fileName << " " << right << flush;
auto [X, y] = datasets.getTensors(fileName); auto result = experiment.cross_validation(path, fileName);
auto states = datasets.getStates(fileName);
auto features = datasets.getFeatures(fileName);
auto samples = datasets.getNSamples(fileName);
auto className = datasets.getClassName(fileName);
cout << " (" << setw(5) << samples << "," << setw(3) << features.size() << ") " << flush;
Fold* fold;
if (stratified)
fold = new StratifiedKFold(n_folds, y, seed);
else
fold = new KFold(n_folds, samples, seed);
auto result = platform::cross_validation(fold, model_name, X, y, features, className, states);
result.setDataset(fileName); result.setDataset(fileName);
experiment.setModelVersion(result.getModelVersion());
experiment.addResult(result); experiment.addResult(result);
delete fold; cout << endl;
} }
experiment.setDuration(timer.getDuration()); experiment.setDuration(timer.getDuration());
if (saveResults)
experiment.save(PATH_RESULTS); experiment.save(PATH_RESULTS);
else
experiment.show();
cout << "Done!" << endl; cout << "Done!" << endl;
return 0; return 0;
} }

View File

@@ -2,6 +2,17 @@
using namespace torch; using namespace torch;
vector<string> split(string text, char delimiter)
{
vector<string> result;
stringstream ss(text);
string token;
while (getline(ss, token, delimiter)) {
result.push_back(token);
}
return result;
}
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> Xd; vector<mdlp::labels_t> Xd;

View File

@@ -11,6 +11,7 @@ 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);
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, string className);