Files
BayesNet/html/bayesnet/classifiers/Classifier.cc.gcov.html
2024-05-06 17:56:00 +02:00

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

Generated by: LCOV version 2.0-1

</html>