33 KiB
33 KiB
<html lang="en">
<head>
</head>
</html>
LCOV - code coverage report | |||||||||||||||||||||||||
![]() | |||||||||||||||||||||||||
|
|||||||||||||||||||||||||
![]() |
Line data Source code 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 : 9 : namespace bayesnet { 10 : 11 324 : Ensemble::Ensemble(bool predict_voting) : Classifier(Network()), n_models(0), predict_voting(predict_voting) 12 : { 13 : 14 324 : }; 15 : const std::string ENSEMBLE_NOT_FITTED = "Ensemble has not been fitted"; 16 40 : void Ensemble::trainModel(const torch::Tensor& weights) 17 : { 18 40 : n_models = models.size(); 19 660 : for (auto i = 0; i < n_models; ++i) { 20 : // fit with std::vectors 21 620 : models[i]->fit(dataset, features, className, states); 22 : } 23 40 : } 24 56 : std::vector<int> Ensemble::compute_arg_max(std::vector<std::vector<double>>& X) 25 : { 26 56 : std::vector<int> y_pred; 27 12400 : for (auto i = 0; i < X.size(); ++i) { 28 12344 : auto max = std::max_element(X[i].begin(), X[i].end()); 29 24688 : y_pred.push_back(std::distance(X[i].begin(), max)); 30 : } 31 112 : return y_pred; 32 56 : } 33 424 : torch::Tensor Ensemble::compute_arg_max(torch::Tensor& X) 34 : { 35 424 : auto y_pred = torch::argmax(X, 1); 36 848 : return y_pred; 37 424 : } 38 164 : 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 164 : auto y_pred_ = votes.accessor<int, 2>(); 42 164 : std::vector<int> y_pred_final; 43 164 : int numClasses = states.at(className).size(); 44 : // votes is m x n_models with the prediction of every model for each sample 45 164 : auto result = torch::zeros({ votes.size(0), numClasses }, torch::kFloat32); 46 164 : auto sum = std::reduce(significanceModels.begin(), significanceModels.end()); 47 42084 : 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 41920 : std::vector<double> n_votes(numClasses, 0.0); 51 375272 : for (int j = 0; j < n_models; ++j) { 52 333352 : n_votes[y_pred_[i][j]] += significanceModels.at(j); 53 : } 54 41920 : result[i] = torch::tensor(n_votes); 55 41920 : } 56 : // To only do one division and gain precision 57 164 : result /= sum; 58 328 : return result; 59 164 : } 60 100 : std::vector<std::vector<double>> Ensemble::predict_proba(std::vector<std::vector<int>>& X) 61 : { 62 100 : if (!fitted) { 63 24 : throw std::logic_error(ENSEMBLE_NOT_FITTED); 64 : } 65 76 : return predict_voting ? predict_average_voting(X) : predict_average_proba(X); 66 : } 67 452 : torch::Tensor Ensemble::predict_proba(torch::Tensor& X) 68 : { 69 452 : if (!fitted) { 70 24 : throw std::logic_error(ENSEMBLE_NOT_FITTED); 71 : } 72 428 : return predict_voting ? predict_average_voting(X) : predict_average_proba(X); 73 : } 74 68 : std::vector<int> Ensemble::predict(std::vector<std::vector<int>>& X) 75 : { 76 68 : auto res = predict_proba(X); 77 104 : return compute_arg_max(res); 78 52 : } 79 436 : torch::Tensor Ensemble::predict(torch::Tensor& X) 80 : { 81 436 : auto res = predict_proba(X); 82 840 : return compute_arg_max(res); 83 420 : } 84 296 : torch::Tensor Ensemble::predict_average_proba(torch::Tensor& X) 85 : { 86 296 : auto n_states = models[0]->getClassNumStates(); 87 296 : torch::Tensor y_pred = torch::zeros({ X.size(1), n_states }, torch::kFloat32); 88 296 : auto threads{ std::vector<std::thread>() }; 89 296 : std::mutex mtx; 90 1764 : for (auto i = 0; i < n_models; ++i) { 91 1468 : threads.push_back(std::thread([&, i]() { 92 1468 : auto ypredict = models[i]->predict_proba(X); 93 1468 : std::lock_guard<std::mutex> lock(mtx); 94 1468 : y_pred += ypredict * significanceModels[i]; 95 1468 : })); 96 : } 97 1764 : for (auto& thread : threads) { 98 1468 : thread.join(); 99 : } 100 296 : auto sum = std::reduce(significanceModels.begin(), significanceModels.end()); 101 296 : y_pred /= sum; 102 592 : return y_pred; 103 296 : } 104 44 : std::vector<std::vector<double>> Ensemble::predict_average_proba(std::vector<std::vector<int>>& X) 105 : { 106 44 : auto n_states = models[0]->getClassNumStates(); 107 44 : std::vector<std::vector<double>> y_pred(X[0].size(), std::vector<double>(n_states, 0.0)); 108 44 : auto threads{ std::vector<std::thread>() }; 109 44 : std::mutex mtx; 110 576 : for (auto i = 0; i < n_models; ++i) { 111 532 : threads.push_back(std::thread([&, i]() { 112 532 : auto ypredict = models[i]->predict_proba(X); 113 532 : assert(ypredict.size() == y_pred.size()); 114 532 : assert(ypredict[0].size() == y_pred[0].size()); 115 532 : 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 110284 : for (auto j = 0; j < ypredict.size(); ++j) { 118 109752 : std::transform(y_pred[j].begin(), y_pred[j].end(), ypredict[j].begin(), y_pred[j].begin(), 119 739464 : [significanceModels = significanceModels[i]](double x, double y) { return x + y * significanceModels; }); 120 : } 121 532 : })); 122 : } 123 576 : for (auto& thread : threads) { 124 532 : thread.join(); 125 : } 126 44 : auto sum = std::reduce(significanceModels.begin(), significanceModels.end()); 127 : //Divide each element of the prediction by the sum of the significances 128 8436 : for (auto j = 0; j < y_pred.size(); ++j) { 129 51544 : std::transform(y_pred[j].begin(), y_pred[j].end(), y_pred[j].begin(), [sum](double x) { return x / sum; }); 130 : } 131 88 : return y_pred; 132 44 : } 133 32 : std::vector<std::vector<double>> Ensemble::predict_average_voting(std::vector<std::vector<int>>& X) 134 : { 135 32 : torch::Tensor Xt = bayesnet::vectorToTensor(X, false); 136 32 : auto y_pred = predict_average_voting(Xt); 137 32 : std::vector<std::vector<double>> result = tensorToVectorDouble(y_pred); 138 64 : return result; 139 32 : } 140 164 : 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 164 : torch::Tensor y_pred = torch::zeros({ X.size(1), n_models }, torch::kInt32); 144 164 : auto threads{ std::vector<std::thread>() }; 145 164 : std::mutex mtx; 146 1380 : for (auto i = 0; i < n_models; ++i) { 147 1216 : threads.push_back(std::thread([&, i]() { 148 1216 : auto ypredict = models[i]->predict(X); 149 1216 : std::lock_guard<std::mutex> lock(mtx); 150 3648 : y_pred.index_put_({ "...", i }, ypredict); 151 2432 : })); 152 : } 153 1380 : for (auto& thread : threads) { 154 1216 : thread.join(); 155 : } 156 328 : return voting(y_pred); 157 164 : } 158 80 : float Ensemble::score(torch::Tensor& X, torch::Tensor& y) 159 : { 160 80 : auto y_pred = predict(X); 161 72 : int correct = 0; 162 22584 : for (int i = 0; i < y_pred.size(0); ++i) { 163 22512 : if (y_pred[i].item<int>() == y[i].item<int>()) { 164 19668 : correct++; 165 : } 166 : } 167 144 : return (double)correct / y_pred.size(0); 168 72 : } 169 52 : float Ensemble::score(std::vector<std::vector<int>>& X, std::vector<int>& y) 170 : { 171 52 : auto y_pred = predict(X); 172 44 : int correct = 0; 173 11164 : for (int i = 0; i < y_pred.size(); ++i) { 174 11120 : if (y_pred[i] == y[i]) { 175 9276 : correct++; 176 : } 177 : } 178 88 : return (double)correct / y_pred.size(); 179 44 : } 180 4 : std::vector<std::string> Ensemble::show() const 181 : { 182 4 : auto result = std::vector<std::string>(); 183 20 : for (auto i = 0; i < n_models; ++i) { 184 16 : auto res = models[i]->show(); 185 16 : result.insert(result.end(), res.begin(), res.end()); 186 16 : } 187 8 : return result; 188 4 : } 189 16 : std::vector<std::string> Ensemble::graph(const std::string& title) const 190 : { 191 16 : auto result = std::vector<std::string>(); 192 108 : for (auto i = 0; i < n_models; ++i) { 193 92 : auto res = models[i]->graph(title + "_" + std::to_string(i)); 194 92 : result.insert(result.end(), res.begin(), res.end()); 195 92 : } 196 32 : return result; 197 16 : } 198 28 : int Ensemble::getNumberOfNodes() const 199 : { 200 28 : int nodes = 0; 201 348 : for (auto i = 0; i < n_models; ++i) { 202 320 : nodes += models[i]->getNumberOfNodes(); 203 : } 204 28 : return nodes; 205 : } 206 28 : int Ensemble::getNumberOfEdges() const 207 : { 208 28 : int edges = 0; 209 348 : for (auto i = 0; i < n_models; ++i) { 210 320 : edges += models[i]->getNumberOfEdges(); 211 : } 212 28 : return edges; 213 : } 214 4 : int Ensemble::getNumberOfStates() const 215 : { 216 4 : int nstates = 0; 217 20 : for (auto i = 0; i < n_models; ++i) { 218 16 : nstates += models[i]->getNumberOfStates(); 219 : } 220 4 : return nstates; 221 : } 222 : } |
![]() |
Generated by: LCOV version 2.0-1 |
</html>