Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
wrapper_metrics.hpp
Go to the documentation of this file.
1#ifndef FSTWRAPPERMETRICS_H
2#define FSTWRAPPERMETRICS_H
3 /*
25=========================================================================
26Copyright and license:
27 * FST4 software (with the exception of any bundled or externally linked
28 third-party components) is Copyright (C) 2000-present Institute of
29 Information Theory and Automation of the Czech Academy of Sciences (UTIA).
30 * FST4 is free software: you can redistribute it and/or modify it under
31 the terms of the GNU Affero General Public License as published by the
32 Free Software Foundation, version 3. The full text of the license is in
33 the file LICENSE in the distribution root and at
34 https://www.gnu.org/licenses/agpl-3.0.html.
35 * FST4 is distributed in the hope that it will be useful, but WITHOUT ANY
36 WARRANTY; without even the implied warranty of MERCHANTABILITY or
37 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
38 License for more details.
39 * Closed-source use without the AGPL's copyleft obligations requires a
40 commercial license from UTIA; in case of interest write to
41 utia@utia.cas.cz. LICENSING.md in the distribution root explains both
42 options, the contributor license agreement that every contribution
43 requires, and the contacts (technical questions and pull requests:
44 somol@utia.cas.cz, with "FST" in the subject line).
45========================================================================== */
46
47#include <vector>
48#include <string>
49#include <utility>
50#include <algorithm>
51#include <cmath>
52
53namespace FST {
54
70
72template<typename RETURNTYPE>
82 std::vector<RETURNTYPE> class_weights;
83 unsigned int f1_positive = 1;
84 bool is_default() const {return metric==Wrapper_Metric::accuracy && class_weights.empty();}
86};
87
89inline const char* wrapper_metric_name(const Wrapper_Metric m)
90{
91 switch(m) {
92 case Wrapper_Metric::accuracy: return "accuracy";
93 case Wrapper_Metric::balanced_accuracy: return "balanced-accuracy";
94 case Wrapper_Metric::f1: return "f1";
95 case Wrapper_Metric::f1_macro: return "f1-macro";
96 case Wrapper_Metric::auc: return "auc";
97 case Wrapper_Metric::r2: return "r2";
98 case Wrapper_Metric::neg_rmse: return "neg-rmse";
99 case Wrapper_Metric::neg_mae: return "neg-mae";
100 }
101 return "accuracy";
102}
103
106inline bool wrapper_metric_from_name(const std::string &name, Wrapper_Metric &m)
107{
108 if(name=="accuracy") m=Wrapper_Metric::accuracy;
109 else if(name=="balanced-accuracy") m=Wrapper_Metric::balanced_accuracy;
110 else if(name=="f1") m=Wrapper_Metric::f1;
111 else if(name=="f1-macro") m=Wrapper_Metric::f1_macro;
112 else if(name=="auc") m=Wrapper_Metric::auc;
113 else if(name=="r2") m=Wrapper_Metric::r2;
114 else if(name=="neg-rmse") m=Wrapper_Metric::neg_rmse;
115 else if(name=="neg-mae") m=Wrapper_Metric::neg_mae;
116 else return false;
117 return true;
118}
119
125template<typename RETURNTYPE, typename DIMTYPE>
126bool metric_from_confusion(RETURNTYPE &result, const Wrapper_Metric metric, const std::vector<RETURNTYPE> &confusion, const DIMTYPE classes, const std::vector<RETURNTYPE> &class_weights, const DIMTYPE f1_positive=1)
127{
128 if(classes<2 || confusion.size()<(size_t)classes*(size_t)classes) return false;
129 const bool weighted=!class_weights.empty();
130 if(weighted && class_weights.size()<(size_t)classes) return false;
131 // cost weights scale whole rows = true-class importance (see Metric_Spec::class_weights);
132 // cell access below applies them on the fly, so every metric below sees weighted counts
133 auto cell=[&](DIMTYPE t, DIMTYPE p)->RETURNTYPE {
134 RETURNTYPE v=confusion[(size_t)t*(size_t)classes+(size_t)p];
135 return weighted ? v*class_weights[t] : v;
136 };
137 switch(metric) {
139 RETURNTYPE diag=0.0, total=0.0;
140 for(DIMTYPE t=0;t<classes;t++) for(DIMTYPE p=0;p<classes;p++) {RETURNTYPE v=cell(t,p); total+=v; if(t==p) diag+=v;}
141 if(!(total>0.0)) return false;
142 result=diag/total;
143 return true;
144 }
146 RETURNTYPE sum=0.0; DIMTYPE present=0;
147 for(DIMTYPE t=0;t<classes;t++) {
148 RETURNTYPE rowsum=0.0;
149 for(DIMTYPE p=0;p<classes;p++) rowsum+=cell(t,p);
150 if(rowsum>0.0) {sum+=cell(t,t)/rowsum; present++;} // classes absent from the test part are skipped
151 }
152 if(present==0) return false;
153 result=sum/(RETURNTYPE)present;
154 return true;
155 }
156 case Wrapper_Metric::f1: { // two-class data; positive class = f1_positive (index 1 unless `--f1-positive` says otherwise)
157 if(classes!=2) return false;
158 if(f1_positive>=classes) return false;
159 const DIMTYPE pos=f1_positive, neg=(DIMTYPE)(1-f1_positive); // two-class only, so the negative class is simply the other index (0 <-> 1)
160 const RETURNTYPE tp=cell(pos,pos), fp=cell(neg,pos), fn=cell(pos,neg);
161 const RETURNTYPE denom=2.0*tp+fp+fn;
162 result=(denom>0.0)?(2.0*tp/denom):(RETURNTYPE)0.0;
163 return true;
164 }
166 RETURNTYPE sum=0.0;
167 for(DIMTYPE c=0;c<classes;c++) {
168 RETURNTYPE tp=cell(c,c), fp=0.0, fn=0.0;
169 for(DIMTYPE t=0;t<classes;t++) if(t!=c) fp+=cell(t,c);
170 for(DIMTYPE p=0;p<classes;p++) if(p!=c) fn+=cell(c,p);
171 const RETURNTYPE denom=2.0*tp+fp+fn;
172 sum+=(denom>0.0)?(2.0*tp/denom):(RETURNTYPE)0.0; // 0/0 contributes 0
173 }
174 result=sum/(RETURNTYPE)classes;
175 return true;
176 }
177 default: return false; // auc needs scores, regression metrics need predictions
178 }
179}
180
189template<typename RETURNTYPE, typename DIMTYPE>
190bool auc_from_scores(RETURNTYPE &result, std::vector<std::pair<RETURNTYPE,DIMTYPE>> &scores)
191{
192 const size_t n=scores.size();
193 if(n==0) return false;
194 size_t n_pos=0;
195 for(size_t i=0;i<n;i++) if(scores[i].second==1) n_pos++;
196 const size_t n_neg=n-n_pos;
197 if(n_pos==0 || n_neg==0) return false;
198 std::stable_sort(scores.begin(),scores.end(),
199 [](const std::pair<RETURNTYPE,DIMTYPE> &a, const std::pair<RETURNTYPE,DIMTYPE> &b){return a.first<b.first;});
200 RETURNTYPE rank_sum_pos=0.0;
201 size_t i=0;
202 while(i<n) { // walk tie groups; group members share the midrank
203 size_t j=i;
204 while(j<n && !(scores[i].first<scores[j].first)) j++;
205 const RETURNTYPE midrank=((RETURNTYPE)(i+1)+(RETURNTYPE)j)/2.0; // ranks are 1-based: (first+last)/2
206 for(size_t k=i;k<j;k++) if(scores[k].second==1) rank_sum_pos+=midrank;
207 i=j;
208 }
209 result=(rank_sum_pos-(RETURNTYPE)n_pos*((RETURNTYPE)n_pos+1.0)/2.0)/((RETURNTYPE)n_pos*(RETURNTYPE)n_neg);
210 return true;
211}
212
221template<typename RETURNTYPE, typename DIMTYPE>
222bool auc_macro_from_scores(RETURNTYPE &result, const std::vector<RETURNTYPE> &scores, const std::vector<DIMTYPE> &labels, const DIMTYPE classes)
223{
224 const size_t n=labels.size();
225 if(n==0 || classes<2 || scores.size()<(size_t)n*(size_t)classes) return false;
226 RETURNTYPE sum=0.0;
227 DIMTYPE defined=0;
228 std::vector<std::pair<RETURNTYPE,DIMTYPE>> pairs;
229 for(DIMTYPE c=0;c<classes;c++)
230 {
231 pairs.clear(); pairs.reserve(n);
232 for(size_t i=0;i<n;i++) pairs.push_back(std::make_pair(scores[i*(size_t)classes+(size_t)c],(DIMTYPE)((labels[i]==c)?1:0)));
233 RETURNTYPE a;
234 if(auc_from_scores<RETURNTYPE,DIMTYPE>(a,pairs)) {sum+=a; defined++;}
235 }
236 if(defined==0) return false;
237 result=sum/(RETURNTYPE)defined;
238 return true;
239}
240
244template<typename RETURNTYPE>
245bool metric_from_predictions(RETURNTYPE &result, const Wrapper_Metric metric, const std::vector<std::pair<RETURNTYPE,RETURNTYPE>> &pairs)
246{
247 const size_t n=pairs.size();
248 if(n==0) return false;
249 switch(metric) {
250 case Wrapper_Metric::r2: {
251 RETURNTYPE mean=0.0;
252 for(size_t i=0;i<n;i++) mean+=pairs[i].first;
253 mean/=(RETURNTYPE)n;
254 RETURNTYPE ss_res=0.0, ss_tot=0.0;
255 for(size_t i=0;i<n;i++) {
256 const RETURNTYPE e=pairs[i].first-pairs[i].second, d=pairs[i].first-mean;
257 ss_res+=e*e; ss_tot+=d*d;
258 }
259 if(!(ss_tot>0.0)) return false; // constant target: R^2 undefined
260 result=1.0-ss_res/ss_tot;
261 return true;
262 }
264 RETURNTYPE ss=0.0;
265 for(size_t i=0;i<n;i++) {const RETURNTYPE e=pairs[i].first-pairs[i].second; ss+=e*e;}
266 result=-std::sqrt(ss/(RETURNTYPE)n);
267 return true;
268 }
270 RETURNTYPE sa=0.0;
271 for(size_t i=0;i<n;i++) sa+=std::fabs(pairs[i].first-pairs[i].second);
272 result=-sa/(RETURNTYPE)n;
273 return true;
274 }
275 default: return false;
276 }
277}
278
279} // namespace
280#endif // FSTWRAPPERMETRICS_H ///:~
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
bool metric_from_confusion(RETURNTYPE &result, const Wrapper_Metric metric, const std::vector< RETURNTYPE > &confusion, const DIMTYPE classes, const std::vector< RETURNTYPE > &class_weights, const DIMTYPE f1_positive=1)
Definition wrapper_metrics.hpp:126
bool wrapper_metric_from_name(const std::string &name, Wrapper_Metric &m)
Parses a CLI metric name (--wrapper-metric value, e.g. "f1-macro") into m.
Definition wrapper_metrics.hpp:106
const char * wrapper_metric_name(const Wrapper_Metric m)
Returns the canonical CLI name (--wrapper-metric value) for m, e.g. "balanced-accuracy"; the inverse ...
Definition wrapper_metrics.hpp:89
Wrapper_Metric
Definition wrapper_metrics.hpp:60
@ neg_rmse
regression: negated root-mean-square prediction error
Definition wrapper_metrics.hpp:67
@ accuracy
classification: fraction correct (cost-weighted if class_weights set)
Definition wrapper_metrics.hpp:61
@ auc
classification: two-class data = area under the ROC curve (exact rank statistic, midrank ties); K>2 =...
Definition wrapper_metrics.hpp:65
@ neg_mae
regression: negated mean absolute prediction error
Definition wrapper_metrics.hpp:68
@ f1
classification, two-class data: F1 of the positive class, which is Metric_Spec::f1_positive (class in...
Definition wrapper_metrics.hpp:63
@ f1_macro
classification: unweighted mean of per-class one-vs-rest F1 (0/0 contributes 0)
Definition wrapper_metrics.hpp:64
@ r2
regression: coefficient of determination (SS_tot around the test-part mean)
Definition wrapper_metrics.hpp:66
@ balanced_accuracy
classification: macro-averaged recall (classes absent from the test part are skipped)
Definition wrapper_metrics.hpp:62
bool metric_from_predictions(RETURNTYPE &result, const Wrapper_Metric metric, const std::vector< std::pair< RETURNTYPE, RETURNTYPE > > &pairs)
Definition wrapper_metrics.hpp:245
bool auc_macro_from_scores(RETURNTYPE &result, const std::vector< RETURNTYPE > &scores, const std::vector< DIMTYPE > &labels, const DIMTYPE classes)
Definition wrapper_metrics.hpp:222
bool auc_from_scores(RETURNTYPE &result, std::vector< std::pair< RETURNTYPE, DIMTYPE > > &scores)
Definition wrapper_metrics.hpp:190
configuration of the wrapper metric; immutable during search, copied by criterion clones (thread-safe...
Definition wrapper_metrics.hpp:73
std::vector< RETURNTYPE > class_weights
Definition wrapper_metrics.hpp:82
bool is_regression_metric() const
true for the three regression metrics (r2/neg_rmse/neg_mae), false for the classification metrics
Definition wrapper_metrics.hpp:85
Wrapper_Metric metric
which metric to compute; accuracy = the default path served by Classifier::test()
Definition wrapper_metrics.hpp:74
unsigned int f1_positive
class index the f1 metric scores as positive (two-class data; --f1-positive). Affects f1 ONLY - f1-ma...
Definition wrapper_metrics.hpp:83
bool is_default() const
true iff this spec is the plain unweighted accuracy path (f1_positive is irrelevant unless metric==f1...
Definition wrapper_metrics.hpp:84
Copyright © 2000–present ÚTIA, Institute of Information Theory and Automation of the Czech Academy of Sciences · Contacts · This site sets no cookies and uses no analytics.