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

Generated by: LCOV version 2.0-1

</html>