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

26 KiB

<html lang="en"> <head> </head>
LCOV - code coverage report
Current view: top level - bayesnet/utils - BayesMetrics.cc (source / functions) Coverage Total Hit
Test: BayesNet Coverage Report Lines: 98.2 % 114 112
Test Date: 2024-05-06 17:54:04 Functions: 100.0 % 11 11
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 "Mst.h"
       8              : #include "BayesMetrics.h"
       9              : namespace bayesnet {
      10              :     //samples is n+1xm tensor used to fit the model
      11         2123 :     Metrics::Metrics(const torch::Tensor& samples, const std::vector<std::string>& features, const std::string& className, const int classNumStates)
      12         2123 :         : samples(samples)
      13         2123 :         , className(className)
      14         2123 :         , features(features)
      15         2123 :         , classNumStates(classNumStates)
      16              :     {
      17         2123 :     }
      18              :     //samples is n+1xm std::vector used to fit the model
      19           96 :     Metrics::Metrics(const std::vector<std::vector<int>>& vsamples, const std::vector<int>& labels, const std::vector<std::string>& features, const std::string& className, const int classNumStates)
      20           96 :         : samples(torch::zeros({ static_cast<int>(vsamples.size() + 1), static_cast<int>(vsamples[0].size()) }, torch::kInt32))
      21           96 :         , className(className)
      22           96 :         , features(features)
      23           96 :         , classNumStates(classNumStates)
      24              :     {
      25          768 :         for (int i = 0; i < vsamples.size(); ++i) {
      26         2688 :             samples.index_put_({ i,  "..." }, torch::tensor(vsamples[i], torch::kInt32));
      27              :         }
      28          384 :         samples.index_put_({ -1, "..." }, torch::tensor(labels, torch::kInt32));
      29          864 :     }
      30          478 :     std::vector<int> Metrics::SelectKBestWeighted(const torch::Tensor& weights, bool ascending, unsigned k)
      31              :     {
      32              :         // Return the K Best features 
      33          478 :         auto n = features.size();
      34          478 :         if (k == 0) {
      35            6 :             k = n;
      36              :         }
      37              :         // compute scores
      38          478 :         scoresKBest.clear();
      39          478 :         featuresKBest.clear();
      40         1434 :         auto label = samples.index({ -1, "..." });
      41        10522 :         for (int i = 0; i < n; ++i) {
      42        30132 :             scoresKBest.push_back(mutualInformation(label, samples.index({ i, "..." }), weights));
      43        10044 :             featuresKBest.push_back(i);
      44              :         }
      45              :         // sort & reduce scores and features
      46          478 :         if (ascending) {
      47           94 :             sort(featuresKBest.begin(), featuresKBest.end(), [&](int i, int j)
      48         2088 :                 { return scoresKBest[i] < scoresKBest[j]; });
      49           94 :             sort(scoresKBest.begin(), scoresKBest.end(), std::less<double>());
      50           94 :             if (k < n) {
      51          154 :                 for (int i = 0; i < n - k; ++i) {
      52          110 :                     featuresKBest.erase(featuresKBest.begin());
      53          110 :                     scoresKBest.erase(scoresKBest.begin());
      54              :                 }
      55              :             }
      56              :         } else {
      57          384 :             sort(featuresKBest.begin(), featuresKBest.end(), [&](int i, int j)
      58        64808 :                 { return scoresKBest[i] > scoresKBest[j]; });
      59          384 :             sort(scoresKBest.begin(), scoresKBest.end(), std::greater<double>());
      60          384 :             featuresKBest.resize(k);
      61          384 :             scoresKBest.resize(k);
      62              :         }
      63          956 :         return featuresKBest;
      64        11000 :     }
      65           48 :     std::vector<double> Metrics::getScoresKBest() const
      66              :     {
      67           48 :         return scoresKBest;
      68              :     }
      69              : 
      70          152 :     torch::Tensor Metrics::conditionalEdge(const torch::Tensor& weights)
      71              :     {
      72          152 :         auto result = std::vector<double>();
      73          152 :         auto source = std::vector<std::string>(features);
      74          152 :         source.push_back(className);
      75          152 :         auto combinations = doCombinations(source);
      76              :         // Compute class prior
      77          152 :         auto margin = torch::zeros({ classNumStates }, torch::kFloat);
      78          828 :         for (int value = 0; value < classNumStates; ++value) {
      79         2704 :             auto mask = samples.index({ -1,  "..." }) == value;
      80          676 :             margin[value] = mask.sum().item<double>() / samples.size(1);
      81          676 :         }
      82         4164 :         for (auto [first, second] : combinations) {
      83         4012 :             int index_first = find(features.begin(), features.end(), first) - features.begin();
      84         4012 :             int index_second = find(features.begin(), features.end(), second) - features.begin();
      85         4012 :             double accumulated = 0;
      86        23820 :             for (int value = 0; value < classNumStates; ++value) {
      87        79232 :                 auto mask = samples.index({ -1, "..." }) == value;
      88        59424 :                 auto first_dataset = samples.index({ index_first, mask });
      89        59424 :                 auto second_dataset = samples.index({ index_second, mask });
      90        39616 :                 auto weights_dataset = weights.index({ mask });
      91        39616 :                 auto mi = mutualInformation(first_dataset, second_dataset, weights_dataset);
      92        19808 :                 auto pb = margin[value].item<double>();
      93        19808 :                 accumulated += pb * mi;
      94        19808 :             }
      95         4012 :             result.push_back(accumulated);
      96         4012 :         }
      97          152 :         long n_vars = source.size();
      98          152 :         auto matrix = torch::zeros({ n_vars, n_vars });
      99          152 :         auto indices = torch::triu_indices(n_vars, n_vars, 1);
     100         4164 :         for (auto i = 0; i < result.size(); ++i) {
     101         4012 :             auto x = indices[0][i];
     102         4012 :             auto y = indices[1][i];
     103         4012 :             matrix[x][y] = result[i];
     104         4012 :             matrix[y][x] = result[i];
     105         4012 :         }
     106          304 :         return matrix;
     107        99868 :     }
     108        41732 :     double Metrics::entropy(const torch::Tensor& feature, const torch::Tensor& weights)
     109              :     {
     110        41732 :         torch::Tensor counts = feature.bincount(weights);
     111        41732 :         double totalWeight = counts.sum().item<double>();
     112        41732 :         torch::Tensor probs = counts.to(torch::kFloat) / totalWeight;
     113        41732 :         torch::Tensor logProbs = torch::log(probs);
     114        41732 :         torch::Tensor entropy = -probs * logProbs;
     115        83464 :         return entropy.nansum().item<double>();
     116        41732 :     }
     117              :     // H(Y|X) = sum_{x in X} p(x) H(Y|X=x)
     118        34276 :     double Metrics::conditionalEntropy(const torch::Tensor& firstFeature, const torch::Tensor& secondFeature, const torch::Tensor& weights)
     119              :     {
     120        34276 :         int numSamples = firstFeature.sizes()[0];
     121        34276 :         torch::Tensor featureCounts = secondFeature.bincount(weights);
     122        34276 :         std::unordered_map<int, std::unordered_map<int, double>> jointCounts;
     123        34276 :         double totalWeight = 0;
     124      6993324 :         for (auto i = 0; i < numSamples; i++) {
     125      6959048 :             jointCounts[secondFeature[i].item<int>()][firstFeature[i].item<int>()] += weights[i].item<double>();
     126      6959048 :             totalWeight += weights[i].item<float>();
     127              :         }
     128        34276 :         if (totalWeight == 0)
     129            0 :             return 0;
     130        34276 :         double entropyValue = 0;
     131       168251 :         for (int value = 0; value < featureCounts.sizes()[0]; ++value) {
     132       133975 :             double p_f = featureCounts[value].item<double>() / totalWeight;
     133       133975 :             double entropy_f = 0;
     134       454356 :             for (auto& [label, jointCount] : jointCounts[value]) {
     135       320381 :                 double p_l_f = jointCount / featureCounts[value].item<double>();
     136       320381 :                 if (p_l_f > 0) {
     137       320381 :                     entropy_f -= p_l_f * log(p_l_f);
     138              :                 } else {
     139            0 :                     entropy_f = 0;
     140              :                 }
     141              :             }
     142       133975 :             entropyValue += p_f * entropy_f;
     143              :         }
     144        34276 :         return entropyValue;
     145        34276 :     }
     146              :     // I(X;Y) = H(Y) - H(Y|X)
     147        34276 :     double Metrics::mutualInformation(const torch::Tensor& firstFeature, const torch::Tensor& secondFeature, const torch::Tensor& weights)
     148              :     {
     149        34276 :         return entropy(firstFeature, weights) - conditionalEntropy(firstFeature, secondFeature, weights);
     150              :     }
     151              :     /*
     152              :     Compute the maximum spanning tree considering the weights as distances
     153              :     and the indices of the weights as nodes of this square matrix using
     154              :     Kruskal algorithm
     155              :     */
     156          148 :     std::vector<std::pair<int, int>> Metrics::maximumSpanningTree(const std::vector<std::string>& features, const torch::Tensor& weights, const int root)
     157              :     {
     158          148 :         auto mst = MST(features, weights, root);
     159          296 :         return mst.maximumSpanningTree();
     160          148 :     }
     161              : }
        

Generated by: LCOV version 2.0-1

</html>