Compile TANNew with poor accuracy
This commit is contained in:
parent
eb1cec58a3
commit
45c1d052ac
@ -1,5 +1,6 @@
|
|||||||
#include "Classifier.h"
|
#include "Classifier.h"
|
||||||
#include "bayesnetUtils.h"
|
#include "bayesnetUtils.h"
|
||||||
|
#include "ArffFiles.h"
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace torch;
|
using namespace torch;
|
||||||
@ -12,7 +13,6 @@ namespace bayesnet {
|
|||||||
this->features = features;
|
this->features = features;
|
||||||
this->className = className;
|
this->className = className;
|
||||||
this->states = states;
|
this->states = states;
|
||||||
cout << "Classifier samples: " << samples.sizes() << endl;
|
|
||||||
checkFitParameters();
|
checkFitParameters();
|
||||||
auto n_classes = states[className].size();
|
auto n_classes = states[className].size();
|
||||||
metrics = Metrics(samples, features, className, n_classes);
|
metrics = Metrics(samples, features, className, n_classes);
|
||||||
@ -115,8 +115,10 @@ namespace bayesnet {
|
|||||||
// Add all nodes to the network
|
// Add all nodes to the network
|
||||||
for (const auto& feature : features) {
|
for (const auto& feature : features) {
|
||||||
model.addNode(feature, states[feature].size());
|
model.addNode(feature, states[feature].size());
|
||||||
|
cout << "-Adding node " << feature << " with " << states[feature].size() << " states" << endl;
|
||||||
}
|
}
|
||||||
model.addNode(className, states[className].size());
|
model.addNode(className, states[className].size());
|
||||||
|
cout << "*Adding class " << className << " with " << states[className].size() << " states" << endl;
|
||||||
}
|
}
|
||||||
int Classifier::getNumberOfNodes()
|
int Classifier::getNumberOfNodes()
|
||||||
{
|
{
|
||||||
@ -139,4 +141,57 @@ namespace bayesnet {
|
|||||||
{
|
{
|
||||||
model.dump_cpt();
|
model.dump_cpt();
|
||||||
}
|
}
|
||||||
|
void Classifier::localDiscretizationProposal(map<string, mdlp::CPPFImdlp*>& discretizers, Tensor& Xf)
|
||||||
|
{
|
||||||
|
// order of local discretization is important. no good 0, 1, 2...
|
||||||
|
auto order = model.topological_sort();
|
||||||
|
auto& nodes = model.getNodes();
|
||||||
|
vector<int> indicesToReDiscretize;
|
||||||
|
auto n_samples = Xf.size(1);
|
||||||
|
bool upgrade = false; // Flag to check if we need to upgrade the model
|
||||||
|
for (auto feature : order) {
|
||||||
|
auto nodeParents = nodes[feature]->getParents();
|
||||||
|
int index = find(features.begin(), features.end(), feature) - features.begin();
|
||||||
|
vector<string> parents;
|
||||||
|
transform(nodeParents.begin(), nodeParents.end(), back_inserter(parents), [](const auto& p) {return p->getName(); });
|
||||||
|
if (parents.size() == 1) continue; // Only has class as parent
|
||||||
|
upgrade = true;
|
||||||
|
// Remove class as parent as it will be added later
|
||||||
|
parents.erase(remove(parents.begin(), parents.end(), className), parents.end());
|
||||||
|
// Get the indices of the parents
|
||||||
|
vector<int> indices;
|
||||||
|
transform(parents.begin(), parents.end(), back_inserter(indices), [&](const auto& p) {return find(features.begin(), features.end(), p) - features.begin(); });
|
||||||
|
// Now we fit the discretizer of the feature conditioned on its parents and the class i.e. discretizer.fit(X[index], X[indices] + y)
|
||||||
|
vector<string> yJoinParents;
|
||||||
|
transform(yv.begin(), yv.end(), back_inserter(yJoinParents), [&](const auto& p) {return to_string(p); });
|
||||||
|
for (auto idx : indices) {
|
||||||
|
for (int i = 0; i < n_samples; ++i) {
|
||||||
|
yJoinParents[i] += to_string(Xv[idx][i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto arff = ArffFiles();
|
||||||
|
auto yxv = arff.factorize(yJoinParents);
|
||||||
|
auto xvf_ptr = Xf.index({ index }).data_ptr<float>();
|
||||||
|
auto xvf = vector<mdlp::precision_t>(xvf_ptr, xvf_ptr + Xf.size(1));
|
||||||
|
discretizers[feature]->fit(xvf, yxv);
|
||||||
|
indicesToReDiscretize.push_back(index);
|
||||||
|
}
|
||||||
|
if (upgrade) {
|
||||||
|
// Discretize again X (only the affected indices) with the new fitted discretizers
|
||||||
|
for (auto index : indicesToReDiscretize) {
|
||||||
|
auto Xt_ptr = Xf.index({ index }).data_ptr<float>();
|
||||||
|
auto Xt = vector<float>(Xt_ptr, Xt_ptr + Xf.size(1));
|
||||||
|
Xv[index] = discretizers[features[index]]->transform(Xt);
|
||||||
|
auto xStates = vector<int>(discretizers[features[index]]->getCutPoints().size() + 1);
|
||||||
|
iota(xStates.begin(), xStates.end(), 0);
|
||||||
|
states[features[index]] = xStates;
|
||||||
|
}
|
||||||
|
// Now we fit the model again with the new values
|
||||||
|
cout << "Classifier: Upgrading model" << endl;
|
||||||
|
// To update the nodes states
|
||||||
|
addNodes();
|
||||||
|
model.fit(Xv, yv, features, className);
|
||||||
|
cout << "Classifier: Model upgraded" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@
|
|||||||
#include "BaseClassifier.h"
|
#include "BaseClassifier.h"
|
||||||
#include "Network.h"
|
#include "Network.h"
|
||||||
#include "BayesMetrics.h"
|
#include "BayesMetrics.h"
|
||||||
|
#include "CPPFImdlp.h"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace torch;
|
using namespace torch;
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ namespace bayesnet {
|
|||||||
map<string, vector<int>> states;
|
map<string, vector<int>> states;
|
||||||
void checkFitParameters();
|
void checkFitParameters();
|
||||||
virtual void train() = 0;
|
virtual void train() = 0;
|
||||||
|
void localDiscretizationProposal(map<string, mdlp::CPPFImdlp*>& discretizers, Tensor& Xf);
|
||||||
public:
|
public:
|
||||||
Classifier(Network model);
|
Classifier(Network model);
|
||||||
virtual ~Classifier() = default;
|
virtual ~Classifier() = default;
|
||||||
|
@ -305,10 +305,7 @@ namespace bayesnet {
|
|||||||
map<string, int> evidence;
|
map<string, int> evidence;
|
||||||
for (int i = 0; i < sample.size(0); ++i) {
|
for (int i = 0; i < sample.size(0); ++i) {
|
||||||
evidence[features[i]] = sample[i].item<int>();
|
evidence[features[i]] = sample[i].item<int>();
|
||||||
cout << "Evidence: " << features[i] << " = " << sample[i].item<int>() << endl;
|
|
||||||
}
|
}
|
||||||
cout << "BEfore exact inference" << endl;
|
|
||||||
|
|
||||||
return exactInference(evidence);
|
return exactInference(evidence);
|
||||||
}
|
}
|
||||||
double Network::computeFactor(map<string, int>& completeEvidence)
|
double Network::computeFactor(map<string, int>& completeEvidence)
|
||||||
|
@ -19,10 +19,6 @@ namespace bayesnet {
|
|||||||
mi.push_back({ i, mi_value });
|
mi.push_back({ i, mi_value });
|
||||||
}
|
}
|
||||||
sort(mi.begin(), mi.end(), [](const auto& left, const auto& right) {return left.second < right.second;});
|
sort(mi.begin(), mi.end(), [](const auto& left, const auto& right) {return left.second < right.second;});
|
||||||
cout << "MI: " << endl;
|
|
||||||
for (int i = 0; i < mi.size(); ++i) {
|
|
||||||
cout << mi[i].first << " " << mi[i].second << endl;
|
|
||||||
}
|
|
||||||
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();
|
||||||
|
@ -1,93 +1,42 @@
|
|||||||
#include "TANNew.h"
|
#include "TANNew.h"
|
||||||
#include "ArffFiles.h"
|
|
||||||
|
|
||||||
namespace bayesnet {
|
namespace bayesnet {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
TANNew::TANNew() : TAN(), n_features{ 0 } {}
|
TANNew::TANNew() : TAN() {}
|
||||||
TANNew::~TANNew() {}
|
TANNew::~TANNew() {}
|
||||||
TANNew& TANNew::fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states)
|
TANNew& TANNew::fit(torch::Tensor& X_, torch::Tensor& y_, vector<string>& features_, string className_, map<string, vector<int>>& states_)
|
||||||
{
|
{
|
||||||
n_features = features.size();
|
Xf = X_;
|
||||||
this->Xf = torch::transpose(X, 0, 1); // now it is mxn as X comes in nxm
|
y = y_;
|
||||||
this->y = y;
|
features = features_;
|
||||||
this->features = features;
|
className = className_;
|
||||||
this->className = className;
|
|
||||||
Xv = vector<vector<int>>();
|
Xv = vector<vector<int>>();
|
||||||
auto Xvf = vector<vector<float>>();
|
|
||||||
yv = vector<int>(y.data_ptr<int>(), y.data_ptr<int>() + y.size(0));
|
yv = vector<int>(y.data_ptr<int>(), y.data_ptr<int>() + y.size(0));
|
||||||
|
// discretize input data by feature(row)
|
||||||
for (int i = 0; i < features.size(); ++i) {
|
for (int i = 0; i < features.size(); ++i) {
|
||||||
auto* discretizer = new mdlp::CPPFImdlp();
|
auto* discretizer = new mdlp::CPPFImdlp();
|
||||||
auto Xt_ptr = X.index({ i }).data_ptr<float>();
|
auto Xt_ptr = Xf.index({ i }).data_ptr<float>();
|
||||||
auto Xt = vector<float>(Xt_ptr, Xt_ptr + X.size(1));
|
auto Xt = vector<float>(Xt_ptr, Xt_ptr + Xf.size(1));
|
||||||
Xvf.push_back(Xt);
|
|
||||||
discretizer->fit(Xt, yv);
|
discretizer->fit(Xt, yv);
|
||||||
Xv.push_back(discretizer->transform(Xt));
|
Xv.push_back(discretizer->transform(Xt));
|
||||||
auto xStates = vector<int>(discretizer->getCutPoints().size() + 1);
|
auto xStates = vector<int>(discretizer->getCutPoints().size() + 1);
|
||||||
iota(xStates.begin(), xStates.end(), 0);
|
iota(xStates.begin(), xStates.end(), 0);
|
||||||
this->states[features[i]] = xStates;
|
states[features[i]] = xStates;
|
||||||
discretizers[features[i]] = discretizer;
|
discretizers[features[i]] = discretizer;
|
||||||
}
|
}
|
||||||
int n_classes = torch::max(y).item<int>() + 1;
|
int n_classes = torch::max(y).item<int>() + 1;
|
||||||
auto yStates = vector<int>(n_classes);
|
auto yStates = vector<int>(n_classes);
|
||||||
iota(yStates.begin(), yStates.end(), 0);
|
iota(yStates.begin(), yStates.end(), 0);
|
||||||
this->states[className] = yStates;
|
states[className] = yStates;
|
||||||
// Now we have standard TAN and now we implement the proposal
|
// Now we have standard TAN and now we implement the proposal
|
||||||
// 1st we need to fit the model to build the TAN structure
|
// 1st we need to fit the model to build the TAN structure
|
||||||
cout << "TANNew: Fitting model" << endl;
|
cout << "TANNew: Fitting model" << endl;
|
||||||
TAN::fit(Xv, yv, features, className, this->states);
|
TAN::fit(Xv, yv, features, className, states);
|
||||||
cout << "TANNew: Model fitted" << endl;
|
cout << "TANNew: Model fitted" << endl;
|
||||||
// order of local discretization is important. no good 0, 1, 2...
|
localDiscretizationProposal(discretizers, Xf);
|
||||||
auto edges = model.getEdges();
|
|
||||||
auto order = model.topological_sort();
|
|
||||||
auto& nodes = model.getNodes();
|
|
||||||
vector<int> indicesToReDiscretize;
|
|
||||||
bool upgrade = false; // Flag to check if we need to upgrade the model
|
|
||||||
for (auto feature : order) {
|
|
||||||
auto nodeParents = nodes[feature]->getParents();
|
|
||||||
int index = find(features.begin(), features.end(), feature) - features.begin();
|
|
||||||
vector<string> parents;
|
|
||||||
transform(nodeParents.begin(), nodeParents.end(), back_inserter(parents), [](const auto& p) {return p->getName(); });
|
|
||||||
if (parents.size() == 1) continue; // Only has class as parent
|
|
||||||
upgrade = true;
|
|
||||||
// Remove class as parent as it will be added later
|
|
||||||
parents.erase(remove(parents.begin(), parents.end(), className), parents.end());
|
|
||||||
// Get the indices of the parents
|
|
||||||
vector<int> indices;
|
|
||||||
transform(parents.begin(), parents.end(), back_inserter(indices), [&](const auto& p) {return find(features.begin(), features.end(), p) - features.begin(); });
|
|
||||||
// Now we fit the discretizer of the feature conditioned on its parents and the class i.e. discretizer.fit(X[index], X[indices] + y)
|
|
||||||
vector<string> yJoinParents;
|
|
||||||
transform(yv.begin(), yv.end(), back_inserter(yJoinParents), [&](const auto& p) {return to_string(p); });
|
|
||||||
for (auto idx : indices) {
|
|
||||||
for (int i = 0; i < Xvf[idx].size(); ++i) {
|
|
||||||
yJoinParents[i] += to_string(Xv[idx][i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto arff = ArffFiles();
|
|
||||||
auto yxv = arff.factorize(yJoinParents);
|
|
||||||
discretizers[feature]->fit(Xvf[index], yxv);
|
|
||||||
indicesToReDiscretize.push_back(index);
|
|
||||||
}
|
|
||||||
if (upgrade) {
|
|
||||||
// Discretize again X (only the affected indices) with the new fitted discretizers
|
|
||||||
for (auto index : indicesToReDiscretize) {
|
|
||||||
auto Xt_ptr = X.index({ index }).data_ptr<float>();
|
|
||||||
auto Xt = vector<float>(Xt_ptr, Xt_ptr + X.size(1));
|
|
||||||
Xv[index] = discretizers[features[index]]->transform(Xt);
|
|
||||||
auto xStates = vector<int>(discretizers[features[index]]->getCutPoints().size() + 1);
|
|
||||||
iota(xStates.begin(), xStates.end(), 0);
|
|
||||||
this->states[features[index]] = xStates;
|
|
||||||
}
|
|
||||||
// Now we fit the model again with the new values
|
|
||||||
cout << "TANNew: Upgrading model" << endl;
|
|
||||||
model.fit(Xv, yv, features, className);
|
|
||||||
cout << "TANNew: Model upgraded" << endl;
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
void TANNew::train()
|
|
||||||
{
|
|
||||||
TAN::train();
|
|
||||||
}
|
|
||||||
Tensor TANNew::predict(Tensor& X)
|
Tensor TANNew::predict(Tensor& X)
|
||||||
{
|
{
|
||||||
auto Xtd = torch::zeros_like(X, torch::kInt32);
|
auto Xtd = torch::zeros_like(X, torch::kInt32);
|
||||||
|
@ -8,12 +8,10 @@ namespace bayesnet {
|
|||||||
class TANNew : public TAN {
|
class TANNew : public TAN {
|
||||||
private:
|
private:
|
||||||
map<string, mdlp::CPPFImdlp*> discretizers;
|
map<string, mdlp::CPPFImdlp*> discretizers;
|
||||||
int n_features;
|
torch::Tensor Xf; // X continuous nxm tensor
|
||||||
torch::Tensor Xf; // X continuous
|
|
||||||
public:
|
public:
|
||||||
TANNew();
|
TANNew();
|
||||||
virtual ~TANNew();
|
virtual ~TANNew();
|
||||||
void train() override;
|
|
||||||
TANNew& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
TANNew& fit(torch::Tensor& X, torch::Tensor& y, vector<string>& features, string className, map<string, vector<int>>& states) override;
|
||||||
vector<string> graph(const string& name = "TAN") override;
|
vector<string> graph(const string& name = "TAN") override;
|
||||||
Tensor predict(Tensor& X) override;
|
Tensor predict(Tensor& X) override;
|
||||||
|
Loading…
Reference in New Issue
Block a user