Files
BayesNet/html/bayesnet/classifiers/Classifier.cc.gcov.html

29 KiB

<html lang="en"> <head> </head>
LCOV - code coverage report
Current view: top level - bayesnet/classifiers - Classifier.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 126 126
Test Date: 2024-04-21 17:30:26 Functions: 100.0 % 24 24

            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 <sstream>
       8              : #include "bayesnet/utils/bayesnetUtils.h"
       9              : #include "Classifier.h"
      10              : 
      11              : namespace bayesnet {
      12          413 :     Classifier::Classifier(Network model) : model(model), m(0), n(0), metrics(Metrics()), fitted(false) {}
      13              :     const std::string CLASSIFIER_NOT_FITTED = "Classifier has not been fitted";
      14          291 :     Classifier& Classifier::build(const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
      15              :     {
      16          291 :         this->features = features;
      17          291 :         this->className = className;
      18          291 :         this->states = states;
      19          291 :         m = dataset.size(1);
      20          291 :         n = features.size();
      21          291 :         checkFitParameters();
      22          283 :         auto n_classes = states.at(className).size();
      23          283 :         metrics = Metrics(dataset, features, className, n_classes);
      24          283 :         model.initialize();
      25          283 :         buildModel(weights);
      26          283 :         trainModel(weights);
      27          279 :         fitted = true;
      28          279 :         return *this;
      29              :     }
      30           77 :     void Classifier::buildDataset(torch::Tensor& ytmp)
      31              :     {
      32              :         try {
      33           77 :             auto yresized = torch::transpose(ytmp.view({ ytmp.size(0), 1 }), 0, 1);
      34          239 :             dataset = torch::cat({ dataset, yresized }, 0);
      35           77 :         }
      36            4 :         catch (const std::exception& e) {
      37            4 :             std::stringstream oss;
      38            4 :             oss << "* Error in X and y dimensions *\n";
      39            4 :             oss << "X dimensions: " << dataset.sizes() << "\n";
      40            4 :             oss << "y dimensions: " << ytmp.sizes();
      41            4 :             throw std::runtime_error(oss.str());
      42            8 :         }
      43          154 :     }
      44          251 :     void Classifier::trainModel(const torch::Tensor& weights)
      45              :     {
      46          251 :         model.fit(dataset, weights, features, className, states);
      47          251 :     }
      48              :     // X is nxm where n is the number of features and m the number of samples
      49           28 :     Classifier& Classifier::fit(torch::Tensor& X, torch::Tensor& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
      50              :     {
      51           28 :         dataset = X;
      52           28 :         buildDataset(y);
      53           26 :         const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
      54           44 :         return build(features, className, states, weights);
      55           26 :     }
      56              :     // X is nxm where n is the number of features and m the number of samples
      57           32 :     Classifier& Classifier::fit(std::vector<std::vector<int>>& X, std::vector<int>& y, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
      58              :     {
      59           32 :         dataset = torch::zeros({ static_cast<int>(X.size()), static_cast<int>(X[0].size()) }, torch::kInt32);
      60          643 :         for (int i = 0; i < X.size(); ++i) {
      61         2444 :             dataset.index_put_({ i, "..." }, torch::tensor(X[i], torch::kInt32));
      62              :         }
      63           32 :         auto ytmp = torch::tensor(y, torch::kInt32);
      64           32 :         buildDataset(ytmp);
      65           30 :         const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
      66           56 :         return build(features, className, states, weights);
      67          647 :     }
      68           99 :     Classifier& Classifier::fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states)
      69              :     {
      70           99 :         this->dataset = dataset;
      71           99 :         const torch::Tensor weights = torch::full({ dataset.size(1) }, 1.0 / dataset.size(1), torch::kDouble);
      72          198 :         return build(features, className, states, weights);
      73           99 :     }
      74          136 :     Classifier& Classifier::fit(torch::Tensor& dataset, const std::vector<std::string>& features, const std::string& className, std::map<std::string, std::vector<int>>& states, const torch::Tensor& weights)
      75              :     {
      76          136 :         this->dataset = dataset;
      77          136 :         return build(features, className, states, weights);
      78              :     }
      79          291 :     void Classifier::checkFitParameters()
      80              :     {
      81          291 :         if (torch::is_floating_point(dataset)) {
      82            2 :             throw std::invalid_argument("dataset (X, y) must be of type Integer");
      83              :         }
      84          289 :         if (dataset.size(0) - 1 != features.size()) {
      85            2 :             throw std::invalid_argument("Classifier: X " + std::to_string(dataset.size(0) - 1) + " and features " + std::to_string(features.size()) + " must have the same number of features");
      86              :         }
      87          287 :         if (states.find(className) == states.end()) {
      88            2 :             throw std::invalid_argument("class name not found in states");
      89              :         }
      90         9467 :         for (auto feature : features) {
      91         9184 :             if (states.find(feature) == states.end()) {
      92            2 :                 throw std::invalid_argument("feature [" + feature + "] not found in states");
      93              :             }
      94         9184 :         }
      95          283 :     }
      96          245 :     torch::Tensor Classifier::predict(torch::Tensor& X)
      97              :     {
      98          245 :         if (!fitted) {
      99            4 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
     100              :         }
     101          241 :         return model.predict(X);
     102              :     }
     103            4 :     std::vector<int> Classifier::predict(std::vector<std::vector<int>>& X)
     104              :     {
     105            4 :         if (!fitted) {
     106            2 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
     107              :         }
     108            2 :         auto m_ = X[0].size();
     109            2 :         auto n_ = X.size();
     110            2 :         std::vector<std::vector<int>> Xd(n_, std::vector<int>(m_, 0));
     111           10 :         for (auto i = 0; i < n_; i++) {
     112           16 :             Xd[i] = std::vector<int>(X[i].begin(), X[i].end());
     113              :         }
     114            2 :         auto yp = model.predict(Xd);
     115            4 :         return yp;
     116            2 :     }
     117          306 :     torch::Tensor Classifier::predict_proba(torch::Tensor& X)
     118              :     {
     119          306 :         if (!fitted) {
     120            2 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
     121              :         }
     122          304 :         return model.predict_proba(X);
     123              :     }
     124           67 :     std::vector<std::vector<double>> Classifier::predict_proba(std::vector<std::vector<int>>& X)
     125              :     {
     126           67 :         if (!fitted) {
     127            2 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
     128              :         }
     129           65 :         auto m_ = X[0].size();
     130           65 :         auto n_ = X.size();
     131           65 :         std::vector<std::vector<int>> Xd(n_, std::vector<int>(m_, 0));
     132              :         // Convert to nxm vector
     133          974 :         for (auto i = 0; i < n_; i++) {
     134         1818 :             Xd[i] = std::vector<int>(X[i].begin(), X[i].end());
     135              :         }
     136           65 :         auto yp = model.predict_proba(Xd);
     137          130 :         return yp;
     138           65 :     }
     139           28 :     float Classifier::score(torch::Tensor& X, torch::Tensor& y)
     140              :     {
     141           28 :         torch::Tensor y_pred = predict(X);
     142           52 :         return (y_pred == y).sum().item<float>() / y.size(0);
     143           26 :     }
     144            4 :     float Classifier::score(std::vector<std::vector<int>>& X, std::vector<int>& y)
     145              :     {
     146            4 :         if (!fitted) {
     147            2 :             throw std::logic_error(CLASSIFIER_NOT_FITTED);
     148              :         }
     149            2 :         return model.score(X, y);
     150              :     }
     151            6 :     std::vector<std::string> Classifier::show() const
     152              :     {
     153            6 :         return model.show();
     154              :     }
     155          251 :     void Classifier::addNodes()
     156              :     {
     157              :         // Add all nodes to the network
     158         8799 :         for (const auto& feature : features) {
     159         8548 :             model.addNode(feature);
     160              :         }
     161          251 :         model.addNode(className);
     162          251 :     }
     163           40 :     int Classifier::getNumberOfNodes() const
     164              :     {
     165              :         // Features does not include class
     166           40 :         return fitted ? model.getFeatures().size() : 0;
     167              :     }
     168           40 :     int Classifier::getNumberOfEdges() const
     169              :     {
     170           40 :         return fitted ? model.getNumEdges() : 0;
     171              :     }
     172            6 :     int Classifier::getNumberOfStates() const
     173              :     {
     174            6 :         return fitted ? model.getStates() : 0;
     175              :     }
     176           76 :     int Classifier::getClassNumStates() const
     177              :     {
     178           76 :         return fitted ? model.getClassNumStates() : 0;
     179              :     }
     180            1 :     std::vector<std::string> Classifier::topological_order()
     181              :     {
     182            1 :         return model.topological_sort();
     183              :     }
     184            1 :     std::string Classifier::dump_cpt() const
     185              :     {
     186            1 :         return model.dump_cpt();
     187              :     }
     188           19 :     void Classifier::setHyperparameters(const nlohmann::json& hyperparameters)
     189              :     {
     190           19 :         if (!hyperparameters.empty()) {
     191            2 :             throw std::invalid_argument("Invalid hyperparameters" + hyperparameters.dump());
     192              :         }
     193           17 :     }
     194              : }
        

Generated by: LCOV version 2.0-1

</html>