Compare commits
18 Commits
2b7353b9e0
...
solveexcep
Author | SHA1 | Date | |
---|---|---|---|
c35030f137
|
|||
182b07ed90
|
|||
7806f961e2
|
|||
7c3e315ae7
|
|||
284ef6dfd1
|
|||
1c6af619b5
|
|||
86ffdfd6f3
|
|||
d82148079d
|
|||
067430fd1b
|
|||
f5d0d16365 | |||
97ca8ac084
|
|||
1c1385b768
|
|||
35432b6294
|
|||
c59dd30e53
|
|||
d2da0ddb88
|
|||
8066701c3c
|
|||
0f66ac73d0
|
|||
4370bf51d7
|
31
.clang-uml
Normal file
31
.clang-uml
Normal file
@@ -0,0 +1,31 @@
|
||||
compilation_database_dir: build
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
BayesNet:
|
||||
type: class
|
||||
glob:
|
||||
- src/BayesNet/*.cc
|
||||
- src/Platform/*.cc
|
||||
using_namespace: bayesnet
|
||||
include:
|
||||
namespaces:
|
||||
- bayesnet
|
||||
- platform
|
||||
plantuml:
|
||||
after:
|
||||
- "note left of {{ alias(\"MyProjectMain\") }}: Main class of myproject library."
|
||||
sequence:
|
||||
type: sequence
|
||||
glob:
|
||||
- src/Platform/main.cc
|
||||
combine_free_functions_into_file_participants: true
|
||||
using_namespace:
|
||||
- std
|
||||
- bayesnet
|
||||
- platform
|
||||
include:
|
||||
paths:
|
||||
- src/BayesNet
|
||||
- src/Platform
|
||||
start_from:
|
||||
- function: main(int,const char **)
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,3 +35,4 @@ build/
|
||||
*.dSYM/**
|
||||
cmake-build*/**
|
||||
.idea
|
||||
puml/**
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,3 +10,6 @@
|
||||
[submodule "lib/json"]
|
||||
path = lib/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "lib/openXLSX"]
|
||||
path = lib/openXLSX
|
||||
url = https://github.com/troldal/OpenXLSX.git
|
||||
|
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
||||
"-d",
|
||||
"iris",
|
||||
"-m",
|
||||
"KDB",
|
||||
"TANLd",
|
||||
"-s",
|
||||
"271",
|
||||
"-p",
|
||||
@@ -25,17 +25,17 @@
|
||||
"program": "${workspaceFolder}/build/src/Platform/main",
|
||||
"args": [
|
||||
"-m",
|
||||
"BoostAODE",
|
||||
"AODE",
|
||||
"-p",
|
||||
"/Users/rmontanana/Code/discretizbench/datasets",
|
||||
"--discretize",
|
||||
"/home/rmontanana/Code/discretizbench/datasets",
|
||||
"--stratified",
|
||||
"-d",
|
||||
"glass",
|
||||
"--hyperparameters",
|
||||
"{\"repeatSparent\": true, \"maxModels\": 12}"
|
||||
"mfeat-morphological",
|
||||
"--discretize"
|
||||
// "--hyperparameters",
|
||||
// "{\"repeatSparent\": true, \"maxModels\": 12}"
|
||||
],
|
||||
"cwd": "/Users/rmontanana/Code/discretizbench",
|
||||
"cwd": "/home/rmontanana/Code/discretizbench",
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
|
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
project(BayesNet
|
||||
VERSION 0.1.0
|
||||
VERSION 0.2.0
|
||||
DESCRIPTION "Bayesian Network and basic classifiers Library."
|
||||
HOMEPAGE_URL "https://github.com/rmontanana/bayesnet"
|
||||
LANGUAGES CXX
|
||||
@@ -30,7 +30,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
|
||||
option(ENABLE_CLANG_TIDY "Enable to add clang tidy." OFF)
|
||||
option(ENABLE_TESTING "Unit testing build" OFF)
|
||||
option(CODE_COVERAGE "Collect coverage from test library" OFF)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
# CMakes modules
|
||||
# --------------
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
||||
@@ -40,8 +40,7 @@ if (CODE_COVERAGE)
|
||||
enable_testing()
|
||||
include(CodeCoverage)
|
||||
MESSAGE("Code coverage enabled")
|
||||
set(CMAKE_C_FLAGS " ${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -O0")
|
||||
SET(GCC_COVERAGE_LINK_FLAGS " ${GCC_COVERAGE_LINK_FLAGS} -lgcov --coverage")
|
||||
endif (CODE_COVERAGE)
|
||||
|
||||
@@ -55,6 +54,7 @@ endif (ENABLE_CLANG_TIDY)
|
||||
add_git_submodule("lib/mdlp")
|
||||
add_git_submodule("lib/argparse")
|
||||
add_git_submodule("lib/json")
|
||||
add_git_submodule("lib/openXLSX")
|
||||
|
||||
# Subdirectories
|
||||
# --------------
|
||||
|
3
Makefile
3
Makefile
@@ -32,6 +32,9 @@ clean: ## Clean the debug info
|
||||
find . -name "*.gcda" -print0 | xargs -0 rm
|
||||
@echo ">>> Done";
|
||||
|
||||
clang-uml: ## Create uml class and sequence diagrams
|
||||
clang-uml -p --add-compile-flag -I /usr/lib/gcc/x86_64-redhat-linux/8/include/
|
||||
|
||||
debug: ## Build a debug version of the project
|
||||
@echo ">>> Building Debug BayesNet ...";
|
||||
@if [ -d ./build ]; then rm -rf ./build; fi
|
||||
|
12
TAN_iris.dot
12
TAN_iris.dot
@@ -1,12 +0,0 @@
|
||||
digraph BayesNet {
|
||||
label=<BayesNet >
|
||||
fontsize=30
|
||||
fontcolor=blue
|
||||
labelloc=t
|
||||
layout=circo
|
||||
class [shape=circle, fontcolor=red, fillcolor=lightblue, style=filled ]
|
||||
class -> sepallength class -> sepalwidth class -> petallength class -> petalwidth petallength [shape=circle]
|
||||
petallength -> sepallength petalwidth [shape=circle]
|
||||
sepallength [shape=circle]
|
||||
sepallength -> sepalwidth sepalwidth [shape=circle]
|
||||
sepalwidth -> petalwidth }
|
@@ -1 +0,0 @@
|
||||
null
|
BIN
diagrams/BayesNet.pdf
Executable file
BIN
diagrams/BayesNet.pdf
Executable file
Binary file not shown.
1
lib/openXLSX
Submodule
1
lib/openXLSX
Submodule
Submodule lib/openXLSX added at b80da42d14
@@ -10,7 +10,7 @@
|
||||
#include "Folding.h"
|
||||
#include "Models.h"
|
||||
#include "modelRegister.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -195,11 +195,11 @@ int main(int argc, char** argv)
|
||||
Xt.index_put_({ i, "..." }, torch::tensor(Xd[i], torch::kInt32));
|
||||
}
|
||||
float total_score = 0, total_score_train = 0, score_train, score_test;
|
||||
Fold* fold;
|
||||
platform::Fold* fold;
|
||||
if (stratified)
|
||||
fold = new StratifiedKFold(nFolds, y, seed);
|
||||
fold = new platform::StratifiedKFold(nFolds, y, seed);
|
||||
else
|
||||
fold = new KFold(nFolds, y.size(), seed);
|
||||
fold = new platform::KFold(nFolds, y.size(), seed);
|
||||
for (auto i = 0; i < nFolds; ++i) {
|
||||
auto [train, test] = fold->getFold(i);
|
||||
cout << "Fold: " << i + 1 << endl;
|
||||
|
@@ -10,7 +10,6 @@ namespace bayesnet {
|
||||
AODE();
|
||||
virtual ~AODE() {};
|
||||
vector<string> graph(const string& title = "AODE") const override;
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
};
|
||||
}
|
||||
#endif
|
@@ -4,9 +4,9 @@
|
||||
namespace bayesnet {
|
||||
using namespace std;
|
||||
AODELd::AODELd() : Ensemble(), Proposal(dataset, features, className) {}
|
||||
AODELd& AODELd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
||||
AODELd& AODELd::fit(torch::Tensor& X_, torch::Tensor& y_, const vector<string>& features_, const string& className_, map<string, vector<int>>& states_)
|
||||
{
|
||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
||||
checkInput(X_, y_);
|
||||
features = features_;
|
||||
className = className_;
|
||||
Xf = X_;
|
||||
@@ -26,6 +26,7 @@ namespace bayesnet {
|
||||
models.push_back(std::make_unique<SPODELd>(i));
|
||||
}
|
||||
n_models = models.size();
|
||||
significanceModels = vector<double>(n_models, 1.0);
|
||||
}
|
||||
void AODELd::trainModel(const torch::Tensor& weights)
|
||||
{
|
||||
|
@@ -12,11 +12,10 @@ namespace bayesnet {
|
||||
void buildModel(const torch::Tensor& weights) override;
|
||||
public:
|
||||
AODELd();
|
||||
AODELd& fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_) override;
|
||||
AODELd& fit(torch::Tensor& X_, torch::Tensor& y_, const vector<string>& features_, const string& className_, map<string, vector<int>>& states_) override;
|
||||
virtual ~AODELd() = default;
|
||||
vector<string> graph(const string& name = "AODE") const override;
|
||||
vector<string> graph(const string& name = "AODELd") const override;
|
||||
static inline string version() { return "0.0.1"; };
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
};
|
||||
}
|
||||
#endif // !AODELD_H
|
@@ -10,11 +10,11 @@ namespace bayesnet {
|
||||
virtual void trainModel(const torch::Tensor& weights) = 0;
|
||||
public:
|
||||
// X is nxm vector, y is nx1 vector
|
||||
virtual BaseClassifier& fit(vector<vector<int>>& X, vector<int>& y, vector<string>& features, string className, map<string, vector<int>>& states) = 0;
|
||||
virtual BaseClassifier& fit(vector<vector<int>>& X, vector<int>& y, const vector<string>& features, const string& className, map<string, vector<int>>& states) = 0;
|
||||
// X is nxm tensor, y is nx1 tensor
|
||||
virtual BaseClassifier& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) = 0;
|
||||
virtual BaseClassifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states) = 0;
|
||||
virtual BaseClassifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights) = 0;
|
||||
virtual BaseClassifier& fit(torch::Tensor& X, torch::Tensor& y, const vector<string>& features, const string& className, map<string, vector<int>>& states) = 0;
|
||||
virtual BaseClassifier& fit(torch::Tensor& dataset, const vector<string>& features, const string& className, map<string, vector<int>>& states) = 0;
|
||||
virtual BaseClassifier& fit(torch::Tensor& dataset, const vector<string>& features, const string& className, map<string, vector<int>>& states, const torch::Tensor& weights) = 0;
|
||||
virtual ~BaseClassifier() = default;
|
||||
torch::Tensor virtual predict(torch::Tensor& X) = 0;
|
||||
vector<int> virtual predict(vector<vector<int>>& X) = 0;
|
||||
@@ -25,7 +25,7 @@ namespace bayesnet {
|
||||
int virtual getNumberOfStates() const = 0;
|
||||
vector<string> virtual show() const = 0;
|
||||
vector<string> virtual graph(const string& title = "") const = 0;
|
||||
const string inline getVersion() const { return "0.1.0"; };
|
||||
const string inline getVersion() const { return "0.2.0"; };
|
||||
vector<string> virtual topological_order() = 0;
|
||||
void virtual dump_cpt()const = 0;
|
||||
virtual void setHyperparameters(nlohmann::json& hyperparameters) = 0;
|
||||
|
@@ -77,7 +77,6 @@ namespace bayesnet {
|
||||
auto source = vector<string>(features);
|
||||
source.push_back(className);
|
||||
auto combinations = doCombinations(source);
|
||||
double totalWeight = weights.sum().item<double>();
|
||||
// Compute class prior
|
||||
auto margin = torch::zeros({ classNumStates }, torch::kFloat);
|
||||
for (int value = 0; value < classNumStates; ++value) {
|
||||
|
@@ -10,6 +10,9 @@ namespace bayesnet {
|
||||
}
|
||||
void BoostAODE::setHyperparameters(nlohmann::json& hyperparameters)
|
||||
{
|
||||
// Check if hyperparameters are valid
|
||||
const vector<string> validKeys = { "repeatSparent", "maxModels", "ascending" };
|
||||
checkHyperparameters(validKeys, hyperparameters);
|
||||
if (hyperparameters.contains("repeatSparent")) {
|
||||
repeatSparent = hyperparameters["repeatSparent"];
|
||||
}
|
||||
@@ -34,7 +37,6 @@ namespace bayesnet {
|
||||
// Step 0: Set the finish condition
|
||||
// if not repeatSparent a finish condition is run out of features
|
||||
// n_models == maxModels
|
||||
int numClasses = states[className].size();
|
||||
while (!exitCondition) {
|
||||
// Step 1: Build ranking with mutual information
|
||||
auto featureSelection = metrics.SelectKBestWeighted(weights_, ascending, n); // Get all the features sorted
|
||||
@@ -74,7 +76,7 @@ namespace bayesnet {
|
||||
// Step 3.4: Store classifier and its accuracy to weigh its future vote
|
||||
models.push_back(std::move(model));
|
||||
significanceModels.push_back(significance);
|
||||
exitCondition = n_models == maxModels;
|
||||
exitCondition = n_models == maxModels && repeatSparent;
|
||||
}
|
||||
if (featuresUsed.size() != features.size()) {
|
||||
cout << "Warning: BoostAODE did not use all the features" << endl;
|
||||
|
@@ -5,7 +5,7 @@ namespace bayesnet {
|
||||
using namespace torch;
|
||||
|
||||
Classifier::Classifier(Network model) : model(model), m(0), n(0), metrics(Metrics()), fitted(false) {}
|
||||
Classifier& Classifier::build(vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights)
|
||||
Classifier& Classifier::build(const vector<string>& features, const string& className, map<string, vector<int>>& states, const torch::Tensor& weights)
|
||||
{
|
||||
this->features = features;
|
||||
this->className = className;
|
||||
@@ -13,7 +13,7 @@ namespace bayesnet {
|
||||
m = dataset.size(1);
|
||||
n = dataset.size(0) - 1;
|
||||
checkFitParameters();
|
||||
auto n_classes = states[className].size();
|
||||
auto n_classes = states.at(className).size();
|
||||
metrics = Metrics(dataset, features, className, n_classes);
|
||||
model.initialize();
|
||||
buildModel(weights);
|
||||
@@ -39,7 +39,7 @@ namespace bayesnet {
|
||||
model.fit(dataset, weights, features, className, states);
|
||||
}
|
||||
// X is nxm where n is the number of features and m the number of samples
|
||||
Classifier& Classifier::fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states)
|
||||
Classifier& Classifier::fit(torch::Tensor& X, torch::Tensor& y, const vector<string>& features, const string& className, map<string, vector<int>>& states)
|
||||
{
|
||||
dataset = X;
|
||||
buildDataset(y);
|
||||
@@ -47,7 +47,7 @@ namespace bayesnet {
|
||||
return build(features, className, states, weights);
|
||||
}
|
||||
// X is nxm where n is the number of features and m the number of samples
|
||||
Classifier& Classifier::fit(vector<vector<int>>& X, vector<int>& y, vector<string>& features, string className, map<string, vector<int>>& states)
|
||||
Classifier& Classifier::fit(vector<vector<int>>& X, vector<int>& y, const vector<string>& features, const string& className, map<string, vector<int>>& states)
|
||||
{
|
||||
dataset = torch::zeros({ static_cast<int>(X.size()), static_cast<int>(X[0].size()) }, kInt32);
|
||||
for (int i = 0; i < X.size(); ++i) {
|
||||
@@ -58,19 +58,22 @@ namespace bayesnet {
|
||||
const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
|
||||
return build(features, className, states, weights);
|
||||
}
|
||||
Classifier& Classifier::fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states)
|
||||
Classifier& Classifier::fit(torch::Tensor& dataset, const vector<string>& features, const string& className, map<string, vector<int>>& states)
|
||||
{
|
||||
this->dataset = dataset;
|
||||
const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
|
||||
return build(features, className, states, weights);
|
||||
}
|
||||
Classifier& Classifier::fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights)
|
||||
Classifier& Classifier::fit(torch::Tensor& dataset, const vector<string>& features, const string& className, map<string, vector<int>>& states, const torch::Tensor& weights)
|
||||
{
|
||||
this->dataset = dataset;
|
||||
return build(features, className, states, weights);
|
||||
}
|
||||
void Classifier::checkFitParameters()
|
||||
{
|
||||
if (torch::is_floating_point(dataset)) {
|
||||
throw invalid_argument("dataset (X, y) must be of type Integer");
|
||||
}
|
||||
if (n != features.size()) {
|
||||
throw invalid_argument("X " + to_string(n) + " and features " + to_string(features.size()) + " must have the same number of features");
|
||||
}
|
||||
@@ -152,4 +155,18 @@ namespace bayesnet {
|
||||
{
|
||||
model.dump_cpt();
|
||||
}
|
||||
void Classifier::checkHyperparameters(const vector<string>& validKeys, nlohmann::json& hyperparameters)
|
||||
{
|
||||
for (const auto& item : hyperparameters.items()) {
|
||||
if (find(validKeys.begin(), validKeys.end(), item.key()) == validKeys.end()) {
|
||||
throw invalid_argument("Hyperparameter " + item.key() + " is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
void Classifier::setHyperparameters(nlohmann::json& hyperparameters)
|
||||
{
|
||||
// Check if hyperparameters are valid, default is no hyperparameters
|
||||
const vector<string> validKeys = { };
|
||||
checkHyperparameters(validKeys, hyperparameters);
|
||||
}
|
||||
}
|
@@ -11,7 +11,7 @@ namespace bayesnet {
|
||||
class Classifier : public BaseClassifier {
|
||||
private:
|
||||
void buildDataset(torch::Tensor& y);
|
||||
Classifier& build(vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights);
|
||||
Classifier& build(const vector<string>& features, const string& className, map<string, vector<int>>& states, const torch::Tensor& weights);
|
||||
protected:
|
||||
bool fitted;
|
||||
int m, n; // m: number of samples, n: number of features
|
||||
@@ -24,13 +24,14 @@ namespace bayesnet {
|
||||
void checkFitParameters();
|
||||
virtual void buildModel(const torch::Tensor& weights) = 0;
|
||||
void trainModel(const torch::Tensor& weights) override;
|
||||
void checkHyperparameters(const vector<string>& validKeys, nlohmann::json& hyperparameters);
|
||||
public:
|
||||
Classifier(Network model);
|
||||
virtual ~Classifier() = default;
|
||||
Classifier& fit(vector<vector<int>>& X, vector<int>& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||
Classifier& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||
Classifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||
Classifier& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states, const torch::Tensor& weights) override;
|
||||
Classifier& fit(vector<vector<int>>& X, vector<int>& y, const vector<string>& features, const string& className, map<string, vector<int>>& states) override;
|
||||
Classifier& fit(torch::Tensor& X, torch::Tensor& y, const vector<string>& features, const string& className, map<string, vector<int>>& states) override;
|
||||
Classifier& fit(torch::Tensor& dataset, const vector<string>& features, const string& className, map<string, vector<int>>& states) override;
|
||||
Classifier& fit(torch::Tensor& dataset, const vector<string>& features, const string& className, map<string, vector<int>>& states, const torch::Tensor& weights) override;
|
||||
void addNodes();
|
||||
int getNumberOfNodes() const override;
|
||||
int getNumberOfEdges() const override;
|
||||
@@ -42,6 +43,7 @@ namespace bayesnet {
|
||||
vector<string> show() const override;
|
||||
vector<string> topological_order() override;
|
||||
void dump_cpt() const override;
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
@@ -3,7 +3,7 @@
|
||||
namespace bayesnet {
|
||||
using namespace torch;
|
||||
|
||||
Ensemble::Ensemble() : Classifier(Network()) {}
|
||||
Ensemble::Ensemble() : Classifier(Network()), n_models(0) {}
|
||||
|
||||
void Ensemble::trainModel(const torch::Tensor& weights)
|
||||
{
|
||||
@@ -17,9 +17,13 @@ namespace bayesnet {
|
||||
{
|
||||
auto y_pred_ = y_pred.accessor<int, 2>();
|
||||
vector<int> y_pred_final;
|
||||
int numClasses = states.at(className).size();
|
||||
// y_pred is m x n_models with the prediction of every model for each sample
|
||||
for (int i = 0; i < y_pred.size(0); ++i) {
|
||||
vector<double> votes(y_pred.size(1), 0);
|
||||
for (int j = 0; j < y_pred.size(1); ++j) {
|
||||
// votes store in each index (value of class) the significance added by each model
|
||||
// i.e. votes[0] contains how much value has the value 0 of class. That value is generated by the models predictions
|
||||
vector<double> votes(numClasses, 0.0);
|
||||
for (int j = 0; j < n_models; ++j) {
|
||||
votes[y_pred_[i][j]] += significanceModels[j];
|
||||
}
|
||||
// argsort in descending order
|
||||
@@ -34,7 +38,6 @@ namespace bayesnet {
|
||||
throw logic_error("Ensemble has not been fitted");
|
||||
}
|
||||
Tensor y_pred = torch::zeros({ X.size(1), n_models }, kInt32);
|
||||
//Create a threadpool
|
||||
auto threads{ vector<thread>() };
|
||||
mutex mtx;
|
||||
for (auto i = 0; i < n_models; ++i) {
|
||||
|
@@ -4,6 +4,18 @@ namespace bayesnet {
|
||||
using namespace torch;
|
||||
|
||||
KDB::KDB(int k, float theta) : Classifier(Network()), k(k), theta(theta) {}
|
||||
void KDB::setHyperparameters(nlohmann::json& hyperparameters)
|
||||
{
|
||||
// Check if hyperparameters are valid
|
||||
const vector<string> validKeys = { "k", "theta" };
|
||||
checkHyperparameters(validKeys, hyperparameters);
|
||||
if (hyperparameters.contains("k")) {
|
||||
k = hyperparameters["k"];
|
||||
}
|
||||
if (hyperparameters.contains("theta")) {
|
||||
theta = hyperparameters["theta"];
|
||||
}
|
||||
}
|
||||
void KDB::buildModel(const torch::Tensor& weights)
|
||||
{
|
||||
/*
|
||||
|
@@ -16,7 +16,7 @@ namespace bayesnet {
|
||||
public:
|
||||
explicit KDB(int k, float theta = 0.03);
|
||||
virtual ~KDB() {};
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override;
|
||||
vector<string> graph(const string& name = "KDB") const override;
|
||||
};
|
||||
}
|
||||
|
@@ -3,9 +3,9 @@
|
||||
namespace bayesnet {
|
||||
using namespace std;
|
||||
KDBLd::KDBLd(int k) : KDB(k), Proposal(dataset, features, className) {}
|
||||
KDBLd& KDBLd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
||||
KDBLd& KDBLd::fit(torch::Tensor& X_, torch::Tensor& y_, const vector<string>& features_, const string& className_, map<string, vector<int>>& states_)
|
||||
{
|
||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
||||
checkInput(X_, y_);
|
||||
features = features_;
|
||||
className = className_;
|
||||
Xf = X_;
|
||||
|
@@ -10,10 +10,9 @@ namespace bayesnet {
|
||||
public:
|
||||
explicit KDBLd(int k);
|
||||
virtual ~KDBLd() = default;
|
||||
KDBLd& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||
KDBLd& fit(torch::Tensor& X, torch::Tensor& y, const vector<string>& features, const string& className, map<string, vector<int>>& states) override;
|
||||
vector<string> graph(const string& name = "KDB") const override;
|
||||
Tensor predict(Tensor& X) override;
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
static inline string version() { return "0.0.1"; };
|
||||
};
|
||||
}
|
||||
|
@@ -3,8 +3,8 @@
|
||||
#include "Network.h"
|
||||
#include "bayesnetUtils.h"
|
||||
namespace bayesnet {
|
||||
Network::Network() : features(vector<string>()), className(""), classNumStates(0), fitted(false) {}
|
||||
Network::Network(float maxT) : features(vector<string>()), className(""), classNumStates(0), maxThreads(maxT), fitted(false) {}
|
||||
Network::Network() : features(vector<string>()), className(""), classNumStates(0), fitted(false), laplaceSmoothing(0) {}
|
||||
Network::Network(float maxT) : features(vector<string>()), className(""), classNumStates(0), maxThreads(maxT), fitted(false), laplaceSmoothing(0) {}
|
||||
Network::Network(Network& other) : laplaceSmoothing(other.laplaceSmoothing), features(other.features), className(other.className), classNumStates(other.getClassNumStates()), maxThreads(other.
|
||||
getmaxThreads()), fitted(other.fitted)
|
||||
{
|
||||
@@ -174,43 +174,11 @@ namespace bayesnet {
|
||||
{
|
||||
setStates(states);
|
||||
laplaceSmoothing = 1.0 / samples.size(1); // To use in CPT computation
|
||||
int maxThreadsRunning = static_cast<int>(std::thread::hardware_concurrency() * maxThreads);
|
||||
if (maxThreadsRunning < 1) {
|
||||
maxThreadsRunning = 1;
|
||||
}
|
||||
vector<thread> threads;
|
||||
mutex mtx;
|
||||
condition_variable cv;
|
||||
int activeThreads = 0;
|
||||
int nextNodeIndex = 0;
|
||||
while (nextNodeIndex < nodes.size()) {
|
||||
unique_lock<mutex> lock(mtx);
|
||||
cv.wait(lock, [&activeThreads, &maxThreadsRunning]() { return activeThreads < maxThreadsRunning; });
|
||||
threads.emplace_back([this, &nextNodeIndex, &mtx, &cv, &activeThreads, &weights]() {
|
||||
while (true) {
|
||||
unique_lock<mutex> lock(mtx);
|
||||
if (nextNodeIndex >= nodes.size()) {
|
||||
break; // No more work remaining
|
||||
}
|
||||
auto& pair = *std::next(nodes.begin(), nextNodeIndex);
|
||||
++nextNodeIndex;
|
||||
lock.unlock();
|
||||
pair.second->computeCPT(samples, features, laplaceSmoothing, weights);
|
||||
lock.lock();
|
||||
nodes[pair.first] = std::move(pair.second);
|
||||
lock.unlock();
|
||||
}
|
||||
lock_guard<mutex> lock(mtx);
|
||||
--activeThreads;
|
||||
cv.notify_one();
|
||||
});
|
||||
++activeThreads;
|
||||
}
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
for (auto& node : nodes) {
|
||||
node.second->computeCPT(samples, features, laplaceSmoothing, weights);
|
||||
fitted = true;
|
||||
}
|
||||
}
|
||||
torch::Tensor Network::predict_tensor(const torch::Tensor& samples, const bool proba)
|
||||
{
|
||||
if (!fitted) {
|
||||
@@ -399,7 +367,6 @@ namespace bayesnet {
|
||||
auto result = features;
|
||||
result.erase(remove(result.begin(), result.end(), className), result.end());
|
||||
bool ending{ false };
|
||||
int idx = 0;
|
||||
while (!ending) {
|
||||
ending = true;
|
||||
for (auto feature : features) {
|
||||
|
@@ -27,6 +27,7 @@ namespace bayesnet {
|
||||
Network();
|
||||
explicit Network(float);
|
||||
explicit Network(Network&);
|
||||
~Network() = default;
|
||||
torch::Tensor& getSamples();
|
||||
float getmaxThreads();
|
||||
void addNode(const string&);
|
||||
@@ -52,7 +53,7 @@ namespace bayesnet {
|
||||
vector<string> graph(const string& title) const; // Returns a vector of strings representing the graph in graphviz format
|
||||
void initialize();
|
||||
void dump_cpt() const;
|
||||
inline string version() { return "0.1.0"; }
|
||||
inline string version() { return "0.2.0"; }
|
||||
};
|
||||
}
|
||||
#endif
|
@@ -100,7 +100,7 @@ namespace bayesnet {
|
||||
}
|
||||
int name_index = pos - features.begin();
|
||||
for (int n_sample = 0; n_sample < dataset.size(1); ++n_sample) {
|
||||
torch::List<c10::optional<torch::Tensor>> coordinates;
|
||||
c10::List<c10::optional<at::Tensor>> coordinates;
|
||||
coordinates.push_back(dataset.index({ name_index, n_sample }));
|
||||
for (auto parent : parents) {
|
||||
pos = find(features.begin(), features.end(), parent->getName());
|
||||
@@ -118,10 +118,10 @@ namespace bayesnet {
|
||||
}
|
||||
float Node::getFactorValue(map<string, int>& evidence)
|
||||
{
|
||||
torch::List<c10::optional<torch::Tensor>> coordinates;
|
||||
c10::List<c10::optional<at::Tensor>> coordinates;
|
||||
// following predetermined order of indices in the cpTable (see Node.h)
|
||||
coordinates.push_back(torch::tensor(evidence[name]));
|
||||
transform(parents.begin(), parents.end(), back_inserter(coordinates), [&evidence](const auto& parent) { return torch::tensor(evidence[parent->getName()]); });
|
||||
coordinates.push_back(at::tensor(evidence[name]));
|
||||
transform(parents.begin(), parents.end(), back_inserter(coordinates), [&evidence](const auto& parent) { return at::tensor(evidence[parent->getName()]); });
|
||||
return cpTable.index({ coordinates }).item<float>();
|
||||
}
|
||||
vector<string> Node::graph(const string& className)
|
||||
|
@@ -9,6 +9,15 @@ namespace bayesnet {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
void Proposal::checkInput(const torch::Tensor& X, const torch::Tensor& y)
|
||||
{
|
||||
if (!torch::is_floating_point(X)) {
|
||||
throw std::invalid_argument("X must be a floating point tensor");
|
||||
}
|
||||
if (torch::is_floating_point(y)) {
|
||||
throw std::invalid_argument("y must be an integer tensor");
|
||||
}
|
||||
}
|
||||
map<string, vector<int>> Proposal::localDiscretizationProposal(const map<string, vector<int>>& oldStates, Network& model)
|
||||
{
|
||||
// order of local discretization is important. no good 0, 1, 2...
|
||||
@@ -44,15 +53,6 @@ namespace bayesnet {
|
||||
auto xvf_ptr = Xf.index({ index }).data_ptr<float>();
|
||||
auto xvf = vector<mdlp::precision_t>(xvf_ptr, xvf_ptr + Xf.size(1));
|
||||
discretizers[feature]->fit(xvf, yxv);
|
||||
//
|
||||
//
|
||||
//
|
||||
// auto tmp = discretizers[feature]->transform(xvf);
|
||||
// Xv[index] = tmp;
|
||||
// auto xStates = vector<int>(discretizers[pFeatures[index]]->getCutPoints().size() + 1);
|
||||
// iota(xStates.begin(), xStates.end(), 0);
|
||||
// //Update new states of the feature/node
|
||||
// states[feature] = xStates;
|
||||
}
|
||||
if (upgrade) {
|
||||
// Discretize again X (only the affected indices) with the new fitted discretizers
|
||||
|
@@ -13,6 +13,7 @@ namespace bayesnet {
|
||||
Proposal(torch::Tensor& pDataset, vector<string>& features_, string& className_);
|
||||
virtual ~Proposal();
|
||||
protected:
|
||||
void checkInput(const torch::Tensor& X, const torch::Tensor& y);
|
||||
torch::Tensor prepareX(torch::Tensor& X);
|
||||
map<string, vector<int>> localDiscretizationProposal(const map<string, vector<int>>& states, Network& model);
|
||||
map<string, vector<int>> fit_local_discretization(const torch::Tensor& y);
|
||||
|
@@ -12,7 +12,6 @@ namespace bayesnet {
|
||||
explicit SPODE(int root);
|
||||
virtual ~SPODE() {};
|
||||
vector<string> graph(const string& name = "SPODE") const override;
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
};
|
||||
}
|
||||
#endif
|
@@ -3,9 +3,9 @@
|
||||
namespace bayesnet {
|
||||
using namespace std;
|
||||
SPODELd::SPODELd(int root) : SPODE(root), Proposal(dataset, features, className) {}
|
||||
SPODELd& SPODELd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
||||
SPODELd& SPODELd::fit(torch::Tensor& X_, torch::Tensor& y_, const vector<string>& features_, const string& className_, map<string, vector<int>>& states_)
|
||||
{
|
||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
||||
checkInput(X_, y_);
|
||||
features = features_;
|
||||
className = className_;
|
||||
Xf = X_;
|
||||
@@ -18,11 +18,13 @@ namespace bayesnet {
|
||||
states = localDiscretizationProposal(states, model);
|
||||
return *this;
|
||||
}
|
||||
SPODELd& SPODELd::fit(torch::Tensor& dataset, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
||||
SPODELd& SPODELd::fit(torch::Tensor& dataset, const vector<string>& features_, const string& className_, map<string, vector<int>>& states_)
|
||||
{
|
||||
if (!torch::is_floating_point(dataset)) {
|
||||
throw std::runtime_error("Dataset must be a floating point tensor");
|
||||
}
|
||||
Xf = dataset.index({ torch::indexing::Slice(0, dataset.size(0) - 1), "..." }).clone();
|
||||
y = dataset.index({ -1, "..." }).clone();
|
||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
||||
features = features_;
|
||||
className = className_;
|
||||
// Fills vectors Xv & yv with the data from tensors X_ (discretized) & y
|
||||
|
@@ -9,11 +9,10 @@ namespace bayesnet {
|
||||
public:
|
||||
explicit SPODELd(int root);
|
||||
virtual ~SPODELd() = default;
|
||||
SPODELd& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||
SPODELd& fit(torch::Tensor& dataset, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||
SPODELd& fit(torch::Tensor& X, torch::Tensor& y, const vector<string>& features, const string& className, map<string, vector<int>>& states) override;
|
||||
SPODELd& fit(torch::Tensor& dataset, const vector<string>& features, const string& className, map<string, vector<int>>& states) override;
|
||||
vector<string> graph(const string& name = "SPODE") const override;
|
||||
Tensor predict(Tensor& X) override;
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
static inline string version() { return "0.0.1"; };
|
||||
};
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ namespace bayesnet {
|
||||
TAN();
|
||||
virtual ~TAN() {};
|
||||
vector<string> graph(const string& name = "TAN") const override;
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
};
|
||||
}
|
||||
#endif
|
@@ -3,9 +3,9 @@
|
||||
namespace bayesnet {
|
||||
using namespace std;
|
||||
TANLd::TANLd() : TAN(), Proposal(dataset, features, className) {}
|
||||
TANLd& TANLd::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
||||
TANLd& TANLd::fit(torch::Tensor& X_, torch::Tensor& y_, const vector<string>& features_, const string& className_, map<string, vector<int>>& states_)
|
||||
{
|
||||
// This first part should go in a Classifier method called fit_local_discretization o fit_float...
|
||||
checkInput(X_, y_);
|
||||
features = features_;
|
||||
className = className_;
|
||||
Xf = X_;
|
||||
|
@@ -10,11 +10,10 @@ namespace bayesnet {
|
||||
public:
|
||||
TANLd();
|
||||
virtual ~TANLd() = default;
|
||||
TANLd& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||
TANLd& fit(torch::Tensor& X, torch::Tensor& y, const vector<string>& features, const string& className, map<string, vector<int>>& states) override;
|
||||
vector<string> graph(const string& name = "TAN") const override;
|
||||
Tensor predict(Tensor& X) override;
|
||||
static inline string version() { return "0.0.1"; };
|
||||
void setHyperparameters(nlohmann::json& hyperparameters) override {};
|
||||
};
|
||||
}
|
||||
#endif // !TANLD_H
|
@@ -4,9 +4,9 @@ include_directories(${BayesNet_SOURCE_DIR}/lib/Files)
|
||||
include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp)
|
||||
include_directories(${BayesNet_SOURCE_DIR}/lib/argparse/include)
|
||||
include_directories(${BayesNet_SOURCE_DIR}/lib/json/include)
|
||||
add_executable(main main.cc Folding.cc platformUtils.cc Experiment.cc Datasets.cc Models.cc Report.cc)
|
||||
add_executable(manage manage.cc Results.cc Report.cc)
|
||||
add_executable(main main.cc Folding.cc platformUtils.cc Experiment.cc Datasets.cc Models.cc ReportConsole.cc ReportBase.cc)
|
||||
add_executable(manage manage.cc Results.cc ReportConsole.cc ReportExcel.cc ReportBase.cc)
|
||||
add_executable(list list.cc platformUtils Datasets.cc)
|
||||
target_link_libraries(main BayesNet ArffFiles mdlp "${TORCH_LIBRARIES}")
|
||||
target_link_libraries(manage "${TORCH_LIBRARIES}")
|
||||
target_link_libraries(manage "${TORCH_LIBRARIES}" OpenXLSX::OpenXLSX)
|
||||
target_link_libraries(list ArffFiles mdlp "${TORCH_LIBRARIES}")
|
@@ -1,6 +1,7 @@
|
||||
#include "Datasets.h"
|
||||
#include "platformUtils.h"
|
||||
#include "ArffFiles.h"
|
||||
#include <fstream>
|
||||
namespace platform {
|
||||
void Datasets::load()
|
||||
{
|
||||
@@ -212,10 +213,11 @@ namespace platform {
|
||||
{
|
||||
for (int i = 0; i < features.size(); ++i) {
|
||||
states[features[i]] = vector<int>(*max_element(Xd[i].begin(), Xd[i].end()) + 1);
|
||||
iota(begin(states[features[i]]), end(states[features[i]]), 0);
|
||||
auto item = states.at(features[i]);
|
||||
iota(begin(item), end(item), 0);
|
||||
}
|
||||
states[className] = vector<int>(*max_element(yv.begin(), yv.end()) + 1);
|
||||
iota(begin(states[className]), end(states[className]), 0);
|
||||
iota(begin(states.at(className)), end(states.at(className)), 0);
|
||||
}
|
||||
void Dataset::load_arff()
|
||||
{
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#include "Experiment.h"
|
||||
#include "Datasets.h"
|
||||
#include "Models.h"
|
||||
#include "Report.h"
|
||||
|
||||
#include "ReportConsole.h"
|
||||
#include <fstream>
|
||||
namespace platform {
|
||||
using json = nlohmann::json;
|
||||
string get_date()
|
||||
@@ -91,7 +91,7 @@ namespace platform {
|
||||
void Experiment::report()
|
||||
{
|
||||
json data = build_json();
|
||||
Report report(data);
|
||||
ReportConsole report(data);
|
||||
report.show();
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ namespace platform {
|
||||
result.addTimeTrain(train_time[item].item<double>());
|
||||
result.addTimeTest(test_time[item].item<double>());
|
||||
item++;
|
||||
clf.reset();
|
||||
}
|
||||
cout << "end. " << flush;
|
||||
delete fold;
|
||||
@@ -186,6 +187,7 @@ namespace platform {
|
||||
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.setTrainTime(torch::mean(train_time).item<double>()).setTestTime(torch::mean(test_time).item<double>());
|
||||
result.setTestTimeStd(torch::std(test_time).item<double>()).setTrainTimeStd(torch::std(train_time).item<double>());
|
||||
result.setNodes(torch::mean(nodes).item<double>()).setLeaves(torch::mean(edges).item<double>()).setDepth(torch::mean(num_states).item<double>());
|
||||
result.setDataset(fileName);
|
||||
addResult(result);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include "Folding.h"
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
namespace platform {
|
||||
Fold::Fold(int k, int n, int seed) : k(k), n(n), seed(seed)
|
||||
{
|
||||
random_device rd;
|
||||
@@ -93,3 +94,4 @@ pair<vector<int>, vector<int>> StratifiedKFold::getFold(int nFold)
|
||||
}
|
||||
return { train_indices, test_indices };
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
#include <vector>
|
||||
#include <random>
|
||||
using namespace std;
|
||||
|
||||
namespace platform {
|
||||
class Fold {
|
||||
protected:
|
||||
int k;
|
||||
@@ -34,4 +34,5 @@ public:
|
||||
StratifiedKFold(int k, torch::Tensor& y, int seed = -1);
|
||||
pair<vector<int>, vector<int>> getFold(int nFold) override;
|
||||
};
|
||||
}
|
||||
#endif
|
@@ -26,7 +26,7 @@ namespace platform {
|
||||
instance = it->second();
|
||||
// wrap instance in a shared ptr and return
|
||||
if (instance != nullptr)
|
||||
return shared_ptr<bayesnet::BaseClassifier>(instance);
|
||||
return unique_ptr<bayesnet::BaseClassifier>(instance);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ namespace platform {
|
||||
public:
|
||||
static std::string datasets() { return "datasets/"; }
|
||||
static std::string results() { return "results/"; }
|
||||
static std::string excel() { return "excel/"; }
|
||||
};
|
||||
}
|
||||
#endif
|
@@ -1,26 +0,0 @@
|
||||
#ifndef REPORT_H
|
||||
#define REPORT_H
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Colors.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
const int MAXL = 128;
|
||||
namespace platform {
|
||||
using namespace std;
|
||||
class Report {
|
||||
public:
|
||||
explicit Report(json data_) { data = data_; };
|
||||
virtual ~Report() = default;
|
||||
void show();
|
||||
private:
|
||||
void header();
|
||||
void body();
|
||||
void footer();
|
||||
string fromVector(const string& key);
|
||||
json data;
|
||||
double totalScore; // Total score of all results in a report
|
||||
};
|
||||
};
|
||||
#endif
|
37
src/Platform/ReportBase.cc
Normal file
37
src/Platform/ReportBase.cc
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <sstream>
|
||||
#include <locale>
|
||||
#include "ReportBase.h"
|
||||
#include "BestResult.h"
|
||||
|
||||
|
||||
namespace platform {
|
||||
string ReportBase::fromVector(const string& key)
|
||||
{
|
||||
stringstream oss;
|
||||
string sep = "";
|
||||
oss << "[";
|
||||
for (auto& item : data[key]) {
|
||||
oss << sep << item.get<double>();
|
||||
sep = ", ";
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
string ReportBase::fVector(const string& title, const json& data, const int width, const int precision)
|
||||
{
|
||||
stringstream oss;
|
||||
string sep = "";
|
||||
oss << title << "[";
|
||||
for (const auto& item : data) {
|
||||
oss << sep << fixed << setw(width) << setprecision(precision) << item.get<double>();
|
||||
sep = ", ";
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
void ReportBase::show()
|
||||
{
|
||||
header();
|
||||
body();
|
||||
}
|
||||
}
|
23
src/Platform/ReportBase.h
Normal file
23
src/Platform/ReportBase.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef REPORTBASE_H
|
||||
#define REPORTBASE_H
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
namespace platform {
|
||||
using namespace std;
|
||||
class ReportBase {
|
||||
public:
|
||||
explicit ReportBase(json data_) { data = data_; };
|
||||
virtual ~ReportBase() = default;
|
||||
void show();
|
||||
protected:
|
||||
json data;
|
||||
string fromVector(const string& key);
|
||||
string fVector(const string& title, const json& data, const int width, const int precision);
|
||||
virtual void header() = 0;
|
||||
virtual void body() = 0;
|
||||
};
|
||||
};
|
||||
#endif
|
@@ -1,52 +1,24 @@
|
||||
#include <sstream>
|
||||
#include <locale>
|
||||
#include "Report.h"
|
||||
#include "ReportConsole.h"
|
||||
#include "BestResult.h"
|
||||
|
||||
|
||||
namespace platform {
|
||||
string headerLine(const string& text)
|
||||
{
|
||||
int n = MAXL - text.length() - 3;
|
||||
n = n < 0 ? 0 : n;
|
||||
return "* " + text + string(n, ' ') + "*\n";
|
||||
}
|
||||
string Report::fromVector(const string& key)
|
||||
{
|
||||
stringstream oss;
|
||||
string sep = "";
|
||||
oss << "[";
|
||||
for (auto& item : data[key]) {
|
||||
oss << sep << item.get<double>();
|
||||
sep = ", ";
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
string fVector(const string& title, const json& data, const int width, const int precision)
|
||||
{
|
||||
stringstream oss;
|
||||
string sep = "";
|
||||
oss << title << "[";
|
||||
for (const auto& item : data) {
|
||||
oss << sep << fixed << setw(width) << setprecision(precision) << item.get<double>();
|
||||
sep = ", ";
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
void Report::show()
|
||||
{
|
||||
header();
|
||||
body();
|
||||
footer();
|
||||
}
|
||||
struct separated : numpunct<char> {
|
||||
char do_decimal_point() const { return ','; }
|
||||
char do_thousands_sep() const { return '.'; }
|
||||
string do_grouping() const { return "\03"; }
|
||||
};
|
||||
void Report::header()
|
||||
|
||||
string ReportConsole::headerLine(const string& text)
|
||||
{
|
||||
int n = MAXL - text.length() - 3;
|
||||
n = n < 0 ? 0 : n;
|
||||
return "* " + text + string(n, ' ') + "*\n";
|
||||
}
|
||||
|
||||
void ReportConsole::header()
|
||||
{
|
||||
locale mylocale(cout.getloc(), new separated);
|
||||
locale::global(mylocale);
|
||||
@@ -62,12 +34,12 @@ namespace platform {
|
||||
cout << string(MAXL, '*') << endl;
|
||||
cout << endl;
|
||||
}
|
||||
void Report::body()
|
||||
void ReportConsole::body()
|
||||
{
|
||||
cout << Colors::GREEN() << "Dataset Sampl. Feat. Cls Nodes Edges States Score Time Hyperparameters" << endl;
|
||||
cout << "============================== ====== ===== === ========= ========= ========= =============== ================== ===============" << endl;
|
||||
json lastResult;
|
||||
totalScore = 0;
|
||||
double totalScore = 0.0;
|
||||
bool odd = true;
|
||||
for (const auto& r : data["results"]) {
|
||||
auto color = odd ? Colors::CYAN() : Colors::BLUE();
|
||||
@@ -98,9 +70,11 @@ namespace platform {
|
||||
cout << headerLine(fVector("Train times: ", lastResult["times_train"], 10, 3));
|
||||
cout << headerLine(fVector("Test times: ", lastResult["times_test"], 10, 3));
|
||||
cout << string(MAXL, '*') << endl;
|
||||
} else {
|
||||
footer(totalScore);
|
||||
}
|
||||
}
|
||||
void Report::footer()
|
||||
void ReportConsole::footer(double totalScore)
|
||||
{
|
||||
cout << Colors::MAGENTA() << string(MAXL, '*') << endl;
|
||||
auto score = data["score_name"].get<string>();
|
||||
@@ -110,6 +84,5 @@ namespace platform {
|
||||
cout << headerLine(oss.str());
|
||||
}
|
||||
cout << string(MAXL, '*') << endl << Colors::RESET();
|
||||
|
||||
}
|
||||
}
|
22
src/Platform/ReportConsole.h
Normal file
22
src/Platform/ReportConsole.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef REPORTCONSOLE_H
|
||||
#define REPORTCONSOLE_H
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "ReportBase.h"
|
||||
#include "Colors.h"
|
||||
|
||||
namespace platform {
|
||||
using namespace std;
|
||||
const int MAXL = 128;
|
||||
class ReportConsole : public ReportBase{
|
||||
public:
|
||||
explicit ReportConsole(json data_) : ReportBase(data_) {};
|
||||
virtual ~ReportConsole() = default;
|
||||
private:
|
||||
string headerLine(const string& text);
|
||||
void header() override;
|
||||
void body() override;
|
||||
void footer(double totalScore);
|
||||
};
|
||||
};
|
||||
#endif
|
109
src/Platform/ReportExcel.cc
Normal file
109
src/Platform/ReportExcel.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <sstream>
|
||||
#include <locale>
|
||||
#include "ReportExcel.h"
|
||||
#include "BestResult.h"
|
||||
|
||||
|
||||
namespace platform {
|
||||
struct separated : numpunct<char> {
|
||||
char do_decimal_point() const { return ','; }
|
||||
|
||||
char do_thousands_sep() const { return '.'; }
|
||||
|
||||
string do_grouping() const { return "\03"; }
|
||||
};
|
||||
|
||||
void ReportExcel::createFile()
|
||||
{
|
||||
doc.create(Paths::excel() + "some_results.xlsx");
|
||||
wks = doc.workbook().worksheet("Sheet1");
|
||||
wks.setName(data["model"].get<string>());
|
||||
}
|
||||
|
||||
void ReportExcel::closeFile()
|
||||
{
|
||||
doc.save();
|
||||
doc.close();
|
||||
}
|
||||
|
||||
void ReportExcel::header()
|
||||
{
|
||||
locale mylocale(cout.getloc(), new separated);
|
||||
locale::global(mylocale);
|
||||
cout.imbue(mylocale);
|
||||
stringstream oss;
|
||||
wks.cell("A1").value().set(
|
||||
"Report " + data["model"].get<string>() + " ver. " + data["version"].get<string>() + " with " +
|
||||
to_string(data["folds"].get<int>()) + " Folds cross validation and " + to_string(data["seeds"].size()) +
|
||||
" random seeds. " + data["date"].get<string>() + " " + data["time"].get<string>());
|
||||
wks.cell("A2").value() = data["title"].get<string>();
|
||||
wks.cell("A3").value() = "Random seeds: " + fromVector("seeds") + " Stratified: " +
|
||||
(data["stratified"].get<bool>() ? "True" : "False");
|
||||
oss << "Execution took " << setprecision(2) << fixed << data["duration"].get<float>() << " seconds, "
|
||||
<< data["duration"].get<float>() / 3600 << " hours, on " << data["platform"].get<string>();
|
||||
wks.cell("A4").value() = oss.str();
|
||||
wks.cell("A5").value() = "Score is " + data["score_name"].get<string>();
|
||||
}
|
||||
|
||||
void ReportExcel::body()
|
||||
{
|
||||
auto head = vector<string>(
|
||||
{ "Dataset", "Samples", "Features", "Classes", "Nodes", "Edges", "States", "Score", "Score Std.", "Time",
|
||||
"Time Std.", "Hyperparameters" });
|
||||
int col = 1;
|
||||
for (const auto& item : head) {
|
||||
wks.cell(8, col++).value() = item;
|
||||
}
|
||||
int row = 9;
|
||||
col = 1;
|
||||
json lastResult;
|
||||
double totalScore = 0.0;
|
||||
string hyperparameters;
|
||||
for (const auto& r : data["results"]) {
|
||||
wks.cell(row, col).value() = r["dataset"].get<string>();
|
||||
wks.cell(row, col + 1).value() = r["samples"].get<int>();
|
||||
wks.cell(row, col + 2).value() = r["features"].get<int>();
|
||||
wks.cell(row, col + 3).value() = r["classes"].get<int>();
|
||||
wks.cell(row, col + 4).value() = r["nodes"].get<float>();
|
||||
wks.cell(row, col + 5).value() = r["leaves"].get<float>();
|
||||
wks.cell(row, col + 6).value() = r["depth"].get<float>();
|
||||
wks.cell(row, col + 7).value() = r["score"].get<double>();
|
||||
wks.cell(row, col + 8).value() = r["score_std"].get<double>();
|
||||
wks.cell(row, col + 9).value() = r["time"].get<double>();
|
||||
wks.cell(row, col + 10).value() = r["time_std"].get<double>();
|
||||
try {
|
||||
hyperparameters = r["hyperparameters"].get<string>();
|
||||
}
|
||||
catch (const exception& err) {
|
||||
stringstream oss;
|
||||
oss << r["hyperparameters"];
|
||||
hyperparameters = oss.str();
|
||||
}
|
||||
wks.cell(row, col + 11).value() = hyperparameters;
|
||||
lastResult = r;
|
||||
totalScore += r["score"].get<double>();
|
||||
row++;
|
||||
}
|
||||
if (data["results"].size() == 1) {
|
||||
for (const string& group : { "scores_train", "scores_test", "times_train", "times_test" }) {
|
||||
row++;
|
||||
col = 1;
|
||||
wks.cell(row, col).value() = group;
|
||||
for (double item : lastResult[group]) {
|
||||
wks.cell(row, ++col).value() = item;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
footer(totalScore, row);
|
||||
}
|
||||
}
|
||||
|
||||
void ReportExcel::footer(double totalScore, int row)
|
||||
{
|
||||
auto score = data["score_name"].get<string>();
|
||||
if (score == BestResult::scoreName()) {
|
||||
wks.cell(row + 2, 1).value() = score + " compared to " + BestResult::title() + " .: ";
|
||||
wks.cell(row + 2, 5).value() = totalScore / BestResult::score();
|
||||
}
|
||||
}
|
||||
}
|
25
src/Platform/ReportExcel.h
Normal file
25
src/Platform/ReportExcel.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef REPORTEXCEL_H
|
||||
#define REPORTEXCEL_H
|
||||
#include <OpenXLSX.hpp>
|
||||
#include "ReportBase.h"
|
||||
#include "Paths.h"
|
||||
#include "Colors.h"
|
||||
namespace platform {
|
||||
using namespace std;
|
||||
using namespace OpenXLSX;
|
||||
const int MAXLL = 128;
|
||||
class ReportExcel : public ReportBase{
|
||||
public:
|
||||
explicit ReportExcel(json data_) : ReportBase(data_) {createFile();};
|
||||
virtual ~ReportExcel() {closeFile();};
|
||||
private:
|
||||
void createFile();
|
||||
void closeFile();
|
||||
XLDocument doc;
|
||||
XLWorksheet wks;
|
||||
void header() override;
|
||||
void body() override;
|
||||
void footer(double totalScore, int row);
|
||||
};
|
||||
};
|
||||
#endif // !REPORTEXCEL_H
|
@@ -1,7 +1,8 @@
|
||||
#include <filesystem>
|
||||
#include "platformUtils.h"
|
||||
#include "Results.h"
|
||||
#include "Report.h"
|
||||
#include "ReportConsole.h"
|
||||
#include "ReportExcel.h"
|
||||
#include "BestResult.h"
|
||||
#include "Colors.h"
|
||||
namespace platform {
|
||||
@@ -94,21 +95,26 @@ namespace platform {
|
||||
cout << "Invalid index" << endl;
|
||||
return -1;
|
||||
}
|
||||
void Results::report(const int index) const
|
||||
void Results::report(const int index, const bool excelReport) const
|
||||
{
|
||||
cout << Colors::YELLOW() << "Reporting " << files.at(index).getFilename() << endl;
|
||||
auto data = files.at(index).load();
|
||||
Report report(data);
|
||||
report.show();
|
||||
if (excelReport) {
|
||||
ReportExcel reporter(data);
|
||||
reporter.show();
|
||||
} else {
|
||||
ReportConsole reporter(data);
|
||||
reporter.show();
|
||||
}
|
||||
}
|
||||
void Results::menu()
|
||||
{
|
||||
char option;
|
||||
int index;
|
||||
bool finished = false;
|
||||
string filename, line, options = "qldhsr";
|
||||
string filename, line, options = "qldhsre";
|
||||
while (!finished) {
|
||||
cout << Colors::RESET() << "Choose option (quit='q', list='l', delete='d', hide='h', sort='s', report='r'): ";
|
||||
cout << Colors::RESET() << "Choose option (quit='q', list='l', delete='d', hide='h', sort='s', report='r', excel='e'): ";
|
||||
getline(cin, line);
|
||||
if (line.size() == 0)
|
||||
continue;
|
||||
@@ -119,12 +125,14 @@ namespace platform {
|
||||
}
|
||||
option = line[0];
|
||||
} else {
|
||||
if (all_of(line.begin(), line.end(), ::isdigit)) {
|
||||
index = stoi(line);
|
||||
if (index >= 0 && index < files.size()) {
|
||||
report(index);
|
||||
} else {
|
||||
cout << "Invalid option" << endl;
|
||||
report(index, false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cout << "Invalid option" << endl;
|
||||
continue;
|
||||
}
|
||||
switch (option) {
|
||||
@@ -164,7 +172,13 @@ namespace platform {
|
||||
index = getIndex("report");
|
||||
if (index == -1)
|
||||
break;
|
||||
report(index);
|
||||
report(index, false);
|
||||
break;
|
||||
case 'e':
|
||||
index = getIndex("excel");
|
||||
if (index == -1)
|
||||
break;
|
||||
report(index, true);
|
||||
break;
|
||||
default:
|
||||
cout << "Invalid option" << endl;
|
||||
|
@@ -42,7 +42,7 @@ namespace platform {
|
||||
vector<Result> files;
|
||||
void load(); // Loads the list of results
|
||||
void show() const;
|
||||
void report(const int index) const;
|
||||
void report(const int index, const bool excelReport) const;
|
||||
int getIndex(const string& intent) const;
|
||||
void menu();
|
||||
void sortList();
|
||||
|
@@ -69,11 +69,12 @@ tuple<Tensor, Tensor, vector<string>, string, map<string, vector<int>>> loadData
|
||||
Xd = torch::zeros({ static_cast<int>(Xr[0].size()), static_cast<int>(Xr.size()) }, torch::kInt32);
|
||||
for (int i = 0; i < features.size(); ++i) {
|
||||
states[features[i]] = vector<int>(*max_element(Xr[i].begin(), Xr[i].end()) + 1);
|
||||
iota(begin(states[features[i]]), end(states[features[i]]), 0);
|
||||
auto item = states.at(features[i]);
|
||||
iota(begin(item), end(item), 0);
|
||||
Xd.index_put_({ "...", i }, torch::tensor(Xr[i], torch::kInt32));
|
||||
}
|
||||
states[className] = vector<int>(*max_element(y.begin(), y.end()) + 1);
|
||||
iota(begin(states[className]), end(states[className]), 0);
|
||||
iota(begin(states.at(className)), end(states.at(className)), 0);
|
||||
} else {
|
||||
Xd = torch::zeros({ static_cast<int>(X[0].size()), static_cast<int>(X.size()) }, torch::kFloat32);
|
||||
for (int i = 0; i < features.size(); ++i) {
|
||||
|
Reference in New Issue
Block a user