Files
BayesNet/docs/manual/_ensemble_8cc_source.html

37 KiB

<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US"> <head> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="dynsections.js"></script> <script type="text/javascript" src="clipboard.js"></script> <script type="text/javascript" src="navtreedata.js"></script> <script type="text/javascript" src="navtree.js"></script> <script type="text/javascript" src="resize.js"></script> <script type="text/javascript" src="cookie.js"></script> <script type="text/javascript" src="search/searchdata.js"></script> <script type="text/javascript" src="search/search.js"></script> </head>
BayesNet 1.0.5
Bayesian Network Classifiers using libtorch from scratch
<script type="text/javascript"> /* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */ var searchBox = new SearchBox("searchBox", "search/",'.html'); /* @license-end */ </script> <script type="text/javascript"> /* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */ $(function() { codefold.init(0); }); /* @license-end */ </script> <script type="text/javascript" src="menudata.js"></script> <script type="text/javascript" src="menu.js"></script> <script type="text/javascript"> /* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */ $(function() { initMenu('',true,false,'search.php','Search',true); $(function() { init_search(); }); }); /* @license-end */ </script>
<script type="text/javascript"> /* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */ $(function(){initNavTree('_ensemble_8cc_source.html',''); initResizable(true); }); /* @license-end */ </script>
Loading...
Searching...
No Matches
Ensemble.cc
1// ***************************************************************
2// SPDX-FileCopyrightText: Copyright 2024 Ricardo Montañana Gómez
3// SPDX-FileType: SOURCE
4// SPDX-License-Identifier: MIT
5// ***************************************************************
6
7#include "Ensemble.h"
8
9namespace bayesnet {
10
11 Ensemble::Ensemble(bool predict_voting) : Classifier(Network()), n_models(0), predict_voting(predict_voting)
12 {
13
14 };
15 const std::string ENSEMBLE_NOT_FITTED = "Ensemble has not been fitted";
16 void Ensemble::trainModel(const torch::Tensor& weights)
17 {
18 n_models = models.size();
19 for (auto i = 0; i < n_models; ++i) {
20 // fit with std::vectors
21 models[i]->fit(dataset, features, className, states);
22 }
23 }
24 std::vector<int> Ensemble::compute_arg_max(std::vector<std::vector<double>>& X)
25 {
26 std::vector<int> y_pred;
27 for (auto i = 0; i < X.size(); ++i) {
28 auto max = std::max_element(X[i].begin(), X[i].end());
29 y_pred.push_back(std::distance(X[i].begin(), max));
30 }
31 return y_pred;
32 }
33 torch::Tensor Ensemble::compute_arg_max(torch::Tensor& X)
34 {
35 auto y_pred = torch::argmax(X, 1);
36 return y_pred;
37 }
38 torch::Tensor Ensemble::voting(torch::Tensor& votes)
39 {
40 // Convert m x n_models tensor to a m x n_class_states with voting probabilities
41 auto y_pred_ = votes.accessor<int, 2>();
42 std::vector<int> y_pred_final;
43 int numClasses = states.at(className).size();
44 // votes is m x n_models with the prediction of every model for each sample
45 auto result = torch::zeros({ votes.size(0), numClasses }, torch::kFloat32);
46 auto sum = std::reduce(significanceModels.begin(), significanceModels.end());
47 for (int i = 0; i < votes.size(0); ++i) {
48 // n_votes store in each index (value of class) the significance added by each model
49 // i.e. n_votes[0] contains how much value has the value 0 of class. That value is generated by the models predictions
50 std::vector<double> n_votes(numClasses, 0.0);
51 for (int j = 0; j < n_models; ++j) {
52 n_votes[y_pred_[i][j]] += significanceModels.at(j);
53 }
54 result[i] = torch::tensor(n_votes);
55 }
56 // To only do one division and gain precision
57 result /= sum;
58 return result;
59 }
60 std::vector<std::vector<double>> Ensemble::predict_proba(std::vector<std::vector<int>>& X)
61 {
62 if (!fitted) {
63 throw std::logic_error(ENSEMBLE_NOT_FITTED);
64 }
65 return predict_voting ? predict_average_voting(X) : predict_average_proba(X);
66 }
67 torch::Tensor Ensemble::predict_proba(torch::Tensor& X)
68 {
69 if (!fitted) {
70 throw std::logic_error(ENSEMBLE_NOT_FITTED);
71 }
72 return predict_voting ? predict_average_voting(X) : predict_average_proba(X);
73 }
74 std::vector<int> Ensemble::predict(std::vector<std::vector<int>>& X)
75 {
76 auto res = predict_proba(X);
77 return compute_arg_max(res);
78 }
79 torch::Tensor Ensemble::predict(torch::Tensor& X)
80 {
81 auto res = predict_proba(X);
82 return compute_arg_max(res);
83 }
84 torch::Tensor Ensemble::predict_average_proba(torch::Tensor& X)
85 {
86 auto n_states = models[0]->getClassNumStates();
87 torch::Tensor y_pred = torch::zeros({ X.size(1), n_states }, torch::kFloat32);
88 auto threads{ std::vector<std::thread>() };
89 std::mutex mtx;
90 for (auto i = 0; i < n_models; ++i) {
91 threads.push_back(std::thread([&, i]() {
92 auto ypredict = models[i]->predict_proba(X);
93 std::lock_guard<std::mutex> lock(mtx);
94 y_pred += ypredict * significanceModels[i];
95 }));
96 }
97 for (auto& thread : threads) {
98 thread.join();
99 }
100 auto sum = std::reduce(significanceModels.begin(), significanceModels.end());
101 y_pred /= sum;
102 return y_pred;
103 }
104 std::vector<std::vector<double>> Ensemble::predict_average_proba(std::vector<std::vector<int>>& X)
105 {
106 auto n_states = models[0]->getClassNumStates();
107 std::vector<std::vector<double>> y_pred(X[0].size(), std::vector<double>(n_states, 0.0));
108 auto threads{ std::vector<std::thread>() };
109 std::mutex mtx;
110 for (auto i = 0; i < n_models; ++i) {
111 threads.push_back(std::thread([&, i]() {
112 auto ypredict = models[i]->predict_proba(X);
113 assert(ypredict.size() == y_pred.size());
114 assert(ypredict[0].size() == y_pred[0].size());
115 std::lock_guard<std::mutex> lock(mtx);
116 // Multiply each prediction by the significance of the model and then add it to the final prediction
117 for (auto j = 0; j < ypredict.size(); ++j) {
118 std::transform(y_pred[j].begin(), y_pred[j].end(), ypredict[j].begin(), y_pred[j].begin(),
119 [significanceModels = significanceModels[i]](double x, double y) { return x + y * significanceModels; });
120 }
121 }));
122 }
123 for (auto& thread : threads) {
124 thread.join();
125 }
126 auto sum = std::reduce(significanceModels.begin(), significanceModels.end());
127 //Divide each element of the prediction by the sum of the significances
128 for (auto j = 0; j < y_pred.size(); ++j) {
129 std::transform(y_pred[j].begin(), y_pred[j].end(), y_pred[j].begin(), [sum](double x) { return x / sum; });
130 }
131 return y_pred;
132 }
133 std::vector<std::vector<double>> Ensemble::predict_average_voting(std::vector<std::vector<int>>& X)
134 {
135 torch::Tensor Xt = bayesnet::vectorToTensor(X, false);
136 auto y_pred = predict_average_voting(Xt);
137 std::vector<std::vector<double>> result = tensorToVectorDouble(y_pred);
138 return result;
139 }
140 torch::Tensor Ensemble::predict_average_voting(torch::Tensor& X)
141 {
142 // Build a m x n_models tensor with the predictions of each model
143 torch::Tensor y_pred = torch::zeros({ X.size(1), n_models }, torch::kInt32);
144 auto threads{ std::vector<std::thread>() };
145 std::mutex mtx;
146 for (auto i = 0; i < n_models; ++i) {
147 threads.push_back(std::thread([&, i]() {
148 auto ypredict = models[i]->predict(X);
149 std::lock_guard<std::mutex> lock(mtx);
150 y_pred.index_put_({ "...", i }, ypredict);
151 }));
152 }
153 for (auto& thread : threads) {
154 thread.join();
155 }
156 return voting(y_pred);
157 }
158 float Ensemble::score(torch::Tensor& X, torch::Tensor& y)
159 {
160 auto y_pred = predict(X);
161 int correct = 0;
162 for (int i = 0; i < y_pred.size(0); ++i) {
163 if (y_pred[i].item<int>() == y[i].item<int>()) {
164 correct++;
165 }
166 }
167 return (double)correct / y_pred.size(0);
168 }
169 float Ensemble::score(std::vector<std::vector<int>>& X, std::vector<int>& y)
170 {
171 auto y_pred = predict(X);
172 int correct = 0;
173 for (int i = 0; i < y_pred.size(); ++i) {
174 if (y_pred[i] == y[i]) {
175 correct++;
176 }
177 }
178 return (double)correct / y_pred.size();
179 }
180 std::vector<std::string> Ensemble::show() const
181 {
182 auto result = std::vector<std::string>();
183 for (auto i = 0; i < n_models; ++i) {
184 auto res = models[i]->show();
185 result.insert(result.end(), res.begin(), res.end());
186 }
187 return result;
188 }
189 std::vector<std::string> Ensemble::graph(const std::string& title) const
190 {
191 auto result = std::vector<std::string>();
192 for (auto i = 0; i < n_models; ++i) {
193 auto res = models[i]->graph(title + "_" + std::to_string(i));
194 result.insert(result.end(), res.begin(), res.end());
195 }
196 return result;
197 }
198 int Ensemble::getNumberOfNodes() const
199 {
200 int nodes = 0;
201 for (auto i = 0; i < n_models; ++i) {
202 nodes += models[i]->getNumberOfNodes();
203 }
204 return nodes;
205 }
206 int Ensemble::getNumberOfEdges() const
207 {
208 int edges = 0;
209 for (auto i = 0; i < n_models; ++i) {
210 edges += models[i]->getNumberOfEdges();
211 }
212 return edges;
213 }
214 int Ensemble::getNumberOfStates() const
215 {
216 int nstates = 0;
217 for (auto i = 0; i < n_models; ++i) {
218 nstates += models[i]->getNumberOfStates();
219 }
220 return nstates;
221 }
222}
</html>