BayesNet 1.0.7.
Bayesian Network and basic classifiers Library.
Loading...
Searching...
No Matches
XBAODE.cc
1// ***************************************************************
2// SPDX-FileCopyrightText: Copyright 2025 Ricardo Montañana Gómez
3// SPDX-FileType: SOURCE
4// SPDX-License-Identifier: MIT
5// ***************************************************************
6#include "XBAODE.h"
7#include "bayesnet/classifiers/XSPODE.h"
8#include "bayesnet/utils/TensorUtils.h"
9#include <limits.h>
10#include <random>
11#include <tuple>
12
13namespace bayesnet
14{
15 XBAODE::XBAODE() : Boost(false) {}
16 std::vector<int> XBAODE::initializeModels(const Smoothing_t smoothing)
17 {
18 torch::Tensor weights_ = torch::full({m}, 1.0 / m, torch::kFloat64);
19 std::vector<int> featuresSelected = featureSelection(weights_);
20 for (const int &feature : featuresSelected)
21 {
22 std::unique_ptr<Classifier> model = std::make_unique<XSpode>(feature);
23 model->fit(dataset, features, className, states, weights_, smoothing);
24 add_model(std::move(model), 1.0);
25 }
26 notes.push_back("Used features in initialization: " + std::to_string(featuresSelected.size()) + " of " +
27 std::to_string(features.size()) + " with " + select_features_algorithm);
28 return featuresSelected;
29 }
30 void XBAODE::trainModel(const torch::Tensor &weights, const bayesnet::Smoothing_t smoothing)
31 {
32 X_train_ = TensorUtils::to_matrix(X_train);
33 y_train_ = TensorUtils::to_vector<int>(y_train);
34 if (convergence)
35 {
36 X_test_ = TensorUtils::to_matrix(X_test);
37 y_test_ = TensorUtils::to_vector<int>(y_test);
38 }
39 fitted = true;
40 double alpha_t;
41 torch::Tensor weights_ = torch::full({m}, 1.0 / m, torch::kFloat64);
42 bool finished = false;
43 std::vector<int> featuresUsed;
44 n_models = 0;
45 if (selectFeatures)
46 {
47 featuresUsed = initializeModels(smoothing);
48 auto ypred = predict(X_train_);
49 auto ypred_t = torch::tensor(ypred);
50 std::tie(weights_, alpha_t, finished) = update_weights(y_train, ypred_t, weights_);
51 // Update significance of the models
52 for (const int &feature : featuresUsed)
53 {
54 significanceModels.pop_back();
55 }
56 for (const int &feature : featuresUsed)
57 {
58 significanceModels.push_back(alpha_t);
59 }
60 // VLOG_SCOPE_F(1, "SelectFeatures. alpha_t: %f n_models: %d", alpha_t,
61 // n_models);
62 if (finished)
63 {
64 return;
65 }
66 }
67 int numItemsPack = 0; // The counter of the models inserted in the current pack
68 // Variables to control the accuracy finish condition
69 double priorAccuracy = 0.0;
70 double improvement = 1.0;
71 double convergence_threshold = 1e-4;
72 int tolerance = 0; // number of times the accuracy is lower than the convergence_threshold
73 // Step 0: Set the finish condition
74 // epsilon sub t > 0.5 => inverse the weights_ policy
75 // validation error is not decreasing
76 // run out of features
77 bool ascending = order_algorithm == bayesnet::Orders.ASC;
78 std::mt19937 g{173};
79 while (!finished)
80 {
81 // Step 1: Build ranking with mutual information
82 auto featureSelection = metrics.SelectKBestWeighted(weights_, ascending, n); // Get all the features sorted
83 if (order_algorithm == bayesnet::Orders.RAND)
84 {
85 std::shuffle(featureSelection.begin(), featureSelection.end(), g);
86 }
87 // Remove used features
88 featureSelection.erase(remove_if(featureSelection.begin(), featureSelection.end(),
89 [&](auto x)
90 {
91 return std::find(featuresUsed.begin(), featuresUsed.end(), x) !=
92 featuresUsed.end();
93 }),
94 featureSelection.end());
95 int k = bisection ? pow(2, tolerance) : 1;
96 int counter = 0; // The model counter of the current pack
97 // VLOG_SCOPE_F(1, "counter=%d k=%d featureSelection.size: %zu", counter, k,
98 // featureSelection.size());
99 while (counter++ < k && featureSelection.size() > 0)
100 {
101 auto feature = featureSelection[0];
102 featureSelection.erase(featureSelection.begin());
103 std::unique_ptr<Classifier> model;
104 model = std::make_unique<XSpode>(feature);
105 model->fit(dataset, features, className, states, weights_, smoothing);
106 /*dynamic_cast<XSpode*>(model.get())->fitx(X_train, y_train, weights_,
107 * smoothing); // using exclusive XSpode fit method*/
108 // DEBUG
109 /*std::cout << dynamic_cast<XSpode*>(model.get())->to_string() <<
110 * std::endl;*/
111 // DEBUG
112 std::vector<int> ypred;
113 if (alpha_block)
114 {
115 //
116 // Compute the prediction with the current ensemble + model
117 //
118 // Add the model to the ensemble
119 add_model(std::move(model), 1.0);
120 // Compute the prediction
121 ypred = predict(X_train_);
122 model = std::move(models.back());
123 // Remove the model from the ensemble
124 remove_last_model();
125 }
126 else
127 {
128 ypred = model->predict(X_train_);
129 }
130 // Step 3.1: Compute the classifier amout of say
131 auto ypred_t = torch::tensor(ypred);
132 std::tie(weights_, alpha_t, finished) = update_weights(y_train, ypred_t, weights_);
133 // Step 3.4: Store classifier and its accuracy to weigh its future vote
134 numItemsPack++;
135 featuresUsed.push_back(feature);
136 add_model(std::move(model), alpha_t);
137 // VLOG_SCOPE_F(2, "finished: %d numItemsPack: %d n_models: %d
138 // featuresUsed: %zu", finished, numItemsPack, n_models,
139 // featuresUsed.size());
140 } // End of the pack
141 if (convergence && !finished)
142 {
143 auto y_val_predict = predict(X_test);
144 double accuracy = (y_val_predict == y_test).sum().item<double>() / (double)y_test.size(0);
145 if (priorAccuracy == 0)
146 {
147 priorAccuracy = accuracy;
148 }
149 else
150 {
151 improvement = accuracy - priorAccuracy;
152 }
153 if (improvement < convergence_threshold)
154 {
155 // VLOG_SCOPE_F(3, " (improvement<threshold) tolerance: %d
156 // numItemsPack: %d improvement: %f prior: %f current: %f", tolerance,
157 // numItemsPack, improvement, priorAccuracy, accuracy);
158 tolerance++;
159 }
160 else
161 {
162 // VLOG_SCOPE_F(3, "* (improvement>=threshold) Reset. tolerance: %d
163 // numItemsPack: %d improvement: %f prior: %f current: %f", tolerance,
164 // numItemsPack, improvement, priorAccuracy, accuracy);
165 tolerance = 0; // Reset the counter if the model performs better
166 numItemsPack = 0;
167 }
168 if (convergence_best)
169 {
170 // Keep the best accuracy until now as the prior accuracy
171 priorAccuracy = std::max(accuracy, priorAccuracy);
172 }
173 else
174 {
175 // Keep the last accuray obtained as the prior accuracy
176 priorAccuracy = accuracy;
177 }
178 }
179 // VLOG_SCOPE_F(1, "tolerance: %d featuresUsed.size: %zu features.size:
180 // %zu", tolerance, featuresUsed.size(), features.size());
181 finished = finished || tolerance > maxTolerance || featuresUsed.size() == features.size();
182 }
183 if (tolerance > maxTolerance)
184 {
185 if (numItemsPack < n_models)
186 {
187 notes.push_back("Convergence threshold reached & " + std::to_string(numItemsPack) + " models eliminated");
188 // VLOG_SCOPE_F(4, "Convergence threshold reached & %d models eliminated
189 // of %d", numItemsPack, n_models);
190 for (int i = featuresUsed.size() - 1; i >= featuresUsed.size() - numItemsPack; --i)
191 {
192 remove_last_model();
193 }
194 // VLOG_SCOPE_F(4, "*Convergence threshold %d models left & %d features
195 // used.", n_models, featuresUsed.size());
196 }
197 else
198 {
199 notes.push_back("Convergence threshold reached & 0 models eliminated");
200 // VLOG_SCOPE_F(4, "Convergence threshold reached & 0 models eliminated
201 // n_models=%d numItemsPack=%d", n_models, numItemsPack);
202 }
203 }
204 if (featuresUsed.size() != features.size())
205 {
206 notes.push_back("Used features in train: " + std::to_string(featuresUsed.size()) + " of " +
207 std::to_string(features.size()));
208 status = bayesnet::WARNING;
209 }
210 notes.push_back("Number of models: " + std::to_string(n_models));
211 return;
212 }
213} // namespace bayesnet