# LCOV - BayesNet Coverage Report - bayesnet/ensembles/Ensemble.cc

LCOV - code coverage report

Current view: | top level - bayesnet/ensembles - Ensemble.cc (source / functions) |  |  | Coverage | Total | Hit
Test: | BayesNet Coverage Report |  | Lines: | 100.0 % | 155 | 155
Test Date: | 2024-05-06 17:54:04 |  | Functions: | 100.0 % | 25 | 25
Legend: | Lines: hit not hit |

                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