Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
classifier.hpp
Go to the documentation of this file.
1#ifndef FSTCLASSIFIER_H
2#define FSTCLASSIFIER_H
3 /*
27=========================================================================
28Copyright and license:
29 * FST4 software (with the exception of any bundled or externally linked
30 third-party components) is Copyright (C) 2000-present Institute of
31 Information Theory and Automation of the Czech Academy of Sciences (UTIA).
32 * FST4 is free software: you can redistribute it and/or modify it under
33 the terms of the GNU Affero General Public License as published by the
34 Free Software Foundation, version 3. The full text of the license is in
35 the file LICENSE in the distribution root and at
36 https://www.gnu.org/licenses/agpl-3.0.html.
37 * FST4 is distributed in the hope that it will be useful, but WITHOUT ANY
38 WARRANTY; without even the implied warranty of MERCHANTABILITY or
39 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
40 License for more details.
41 * Closed-source use without the AGPL's copyleft obligations requires a
42 commercial license from UTIA; in case of interest write to
43 utia@utia.cas.cz. LICENSING.md in the distribution root explains both
44 options, the contributor license agreement that every contribution
45 requires, and the contacts (technical questions and pull requests:
46 somol@utia.cas.cz, with "FST" in the subject line).
47========================================================================== */
48
49#include <memory>
50#include <vector>
51#include <utility>
52#include <cassert>
53#include "clonable.hpp"
54#include "error.hpp"
55#include "global.hpp"
56#include "wrapper_metrics.hpp"
57
58/*============== Template parameter type naming conventions ==============
59--------- Numeric types: -------------------------------------------------
60DATATYPE - data sample values - usually real numbers (but may be integers
61 in text processing etc.)
62REALTYPE - must be real numbers - for representing intermediate results of
63 calculations like mean, covariance etc.
64IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
65 extent depends on numbers of samples in data
66DIMTYPE - index values for enumeration of features (dimensions), or classes (not
67 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
68 but be aware of expressions like _classes*_features*_features !
69 in linearized representations of feature matrices for all classes
70BINTYPE - feature selection marker type - represents ca. <10 different feature
71 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
72RETURNTYPE - criterion value: real value, but may be extended in future to support
73 multiple values
74--------- Class types: ---------------------------------------------------
75SUBSET - class of class type Subset
76CLASSIFIER - class implementing interface defined in abstract class Classifier
77EVALUATOR - class implementing interface defined in abstract class Sequential_Step
78DISTANCE - class implementing interface defined in abstract class Distance
79DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
80INTERVALCONTAINER - class of class type Data_Intervaller
81CONTAINER - STL container of class type Data_Interval
82========================================================================== */
83
84namespace FST {
85
96template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
97class Classifier : public Clonable { // abstract class
98public:
99 typedef std::shared_ptr<DATAACCESSOR> PDataAccessor;
100 typedef std::shared_ptr<SUBSET> const PSubset;
101 typedef typename DATAACCESSOR::PPattern PPattern;
102 Classifier() {}
103 virtual ~Classifier() {}
104
109 virtual bool classify(DIMTYPE &cls, const PPattern &pattern) =0;
110 virtual bool train(const PDataAccessor da, const PSubset sub) =0;
117 virtual bool test(RETURNTYPE &result, const PDataAccessor da);
118
121 virtual bool classify_scores(std::vector<RETURNTYPE> &scores, const PPattern &pattern) {(void)scores; (void)pattern; return false;}
122
127 virtual bool test_confusion(std::vector<RETURNTYPE> &confusion, DIMTYPE &classes, const PDataAccessor da);
128
133 virtual bool test_scores(std::vector<std::pair<RETURNTYPE,DIMTYPE>> &scores, const PDataAccessor da);
134
139 virtual bool test_scores_matrix(std::vector<RETURNTYPE> &scores, std::vector<DIMTYPE> &labels, DIMTYPE &classes, const PDataAccessor da);
140
145 virtual bool test_metric(RETURNTYPE &result, const PDataAccessor da, const Metric_Spec<RETURNTYPE> &spec);
146
147 Classifier* clone() const override =0;
148 Classifier* sharing_clone() const override {throw fst_error("Classifier::sharing_clone() not supported, use Classifier::clone() instead.");}
149 Classifier* stateless_clone() const override {throw fst_error("Classifier::stateless_clone() not supported, use Classifier::clone() instead.");}
150};
151
152template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
154{
155 // the canonical accuracy loop (see test_confusion below for the setClass re-issue note)
156 notify("Classifier::test().");
157 assert(da);
158 typename DATAACCESSOR::PPattern p;
159 unsigned long long count=0, correct=0;
160 DIMTYPE clstmp;
161 const DIMTYPE stride=da->getNoOfFeatures();
162 for(DIMTYPE c_test=0;c_test<da->getNoOfClasses();c_test++)
163 {
164 da->setClass(c_test);
165 for(const auto &blk : da->blocks(TEST)) { p=blk.data; const auto s=blk.size;
166 for(auto i=decltype(s){0};i<s;i++)
167 {
168 if(!classify(clstmp,&p[(std::size_t)i*stride])) return false;
169 if(clstmp==c_test) correct++;
170 count++;
171 }
172 da->setClass(c_test); // necessary for the case of da pointing to the same object as the classifier's training accessor
173 }
174 }
175 if(count==0) return false;
176 result=(RETURNTYPE)correct/(RETURNTYPE)count;
177 return true;
178}
179
180template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
181bool Classifier<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::test_confusion(std::vector<RETURNTYPE> &confusion, DIMTYPE &classes, const PDataAccessor da)
182{
183 // the canonical test() loop, tallying a confusion matrix instead of correct/count.
184 // NOTE: the re-issued setClass() after each block is necessary for classifiers whose
185 // classify() iterates a data accessor that may be the same instance (kNN)
186 assert(da);
187 classes=da->getNoOfClasses();
188 if(classes<2) return false;
189 confusion.assign((size_t)classes*(size_t)classes,(RETURNTYPE)0.0);
190 typename DATAACCESSOR::PPattern p;
191 unsigned long long count=0;
192 DIMTYPE clstmp;
193 const DIMTYPE stride=da->getNoOfFeatures();
194 for(DIMTYPE c_test=0;c_test<classes;c_test++)
195 {
196 da->setClass(c_test);
197 for(const auto &blk : da->blocks(TEST)) { p=blk.data; const auto s=blk.size;
198 for(auto i=decltype(s){0};i<s;i++)
199 {
200 if(!classify(clstmp,&p[(std::size_t)i*stride])) return false;
201 if(clstmp>=classes) clstmp=(c_test+1)%classes; // "wrong class" convention (kNN, no neighbours): deterministic error column, keeps the true-class row total intact
202 confusion[(size_t)c_test*(size_t)classes+(size_t)clstmp]+=1.0;
203 count++;
204 }
205 da->setClass(c_test); // necessary for the case of da pointing to the same object as the classifier's training accessor
206 }
207 }
208 return count>0;
209}
210
211template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
212bool Classifier<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::test_scores(std::vector<std::pair<RETURNTYPE,DIMTYPE>> &scores, const PDataAccessor da)
213{
214 // same canonical loop, collecting (positive-class score, true class) pairs (two-class data)
215 assert(da);
216 const DIMTYPE classes=da->getNoOfClasses();
217 if(classes!=2) return false;
218 scores.clear();
219 std::vector<RETURNTYPE> sc;
220 typename DATAACCESSOR::PPattern p;
221 const DIMTYPE stride=da->getNoOfFeatures();
222 for(DIMTYPE c_test=0;c_test<classes;c_test++)
223 {
224 da->setClass(c_test);
225 for(const auto &blk : da->blocks(TEST)) { p=blk.data; const auto s=blk.size;
226 for(auto i=decltype(s){0};i<s;i++)
227 {
228 if(!classify_scores(sc,&p[(std::size_t)i*stride]) || sc.size()<2) return false;
229 scores.push_back(std::make_pair(sc[1]-sc[0],c_test));
230 }
231 da->setClass(c_test); // necessary for the case of da pointing to the same object as the classifier's training accessor
232 }
233 }
234 return !scores.empty();
235}
236
237template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
238bool Classifier<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::test_scores_matrix(std::vector<RETURNTYPE> &scores, std::vector<DIMTYPE> &labels, DIMTYPE &classes, const PDataAccessor da)
239{
240 // same canonical loop as test_scores, keeping every class's score column
241 assert(da);
242 classes=da->getNoOfClasses();
243 if(classes<2) return false;
244 scores.clear(); labels.clear();
245 std::vector<RETURNTYPE> sc;
246 typename DATAACCESSOR::PPattern p;
247 const DIMTYPE stride=da->getNoOfFeatures();
248 for(DIMTYPE c_test=0;c_test<classes;c_test++)
249 {
250 da->setClass(c_test);
251 for(const auto &blk : da->blocks(TEST)) { p=blk.data; const auto s=blk.size;
252 for(auto i=decltype(s){0};i<s;i++)
253 {
254 if(!classify_scores(sc,&p[(std::size_t)i*stride]) || sc.size()<(size_t)classes) return false;
255 for(DIMTYPE c=0;c<classes;c++) scores.push_back(sc[c]);
256 labels.push_back(c_test);
257 }
258 da->setClass(c_test); // necessary for the case of da pointing to the same object as the classifier's training accessor
259 }
260 }
261 return !labels.empty();
262}
263
264template<class RETURNTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
266{
267 if(spec.is_default()) return test(result,da); // plain accuracy, served by test() itself
268 if(spec.is_regression_metric()) return false; // regression metrics apply to Regressor stacks only
269 if(spec.metric==Wrapper_Metric::auc) {
270 if(da->getNoOfClasses()==2) { // two classes: the exact binary AUC
271 std::vector<std::pair<RETURNTYPE,DIMTYPE>> scores;
272 if(!test_scores(scores,da)) return false;
273 return auc_from_scores<RETURNTYPE,DIMTYPE>(result,scores);
274 }
275 std::vector<RETURNTYPE> scores; // K>2: one-vs-rest macro AUC
276 std::vector<DIMTYPE> labels;
277 DIMTYPE classes;
278 if(!test_scores_matrix(scores,labels,classes,da)) return false;
279 return auc_macro_from_scores<RETURNTYPE,DIMTYPE>(result,scores,labels,classes);
280 }
281 std::vector<RETURNTYPE> confusion;
282 DIMTYPE classes;
283 if(!test_confusion(confusion,classes,da)) return false;
284 return metric_from_confusion<RETURNTYPE,DIMTYPE>(result,spec.metric,confusion,classes,spec.class_weights,(DIMTYPE)spec.f1_positive);
285}
286
287} // namespace
288#endif // FSTCLASSIFIER_H ///:~
abstract class, defines interface for classifier implementations (mainly to be used in wrappers)
Definition classifier.hpp:97
virtual bool test_metric(RETURNTYPE &result, const PDataAccessor da, const Metric_Spec< RETURNTYPE > &spec)
Definition classifier.hpp:265
Classifier * clone() const override=0
Classifier * stateless_clone() const override
Definition classifier.hpp:149
virtual bool classify(DIMTYPE &cls, const PPattern &pattern)=0
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying train/test blocks
Definition classifier.hpp:99
virtual bool classify_scores(std::vector< RETURNTYPE > &scores, const PPattern &pattern)
Definition classifier.hpp:121
Classifier * sharing_clone() const override
Definition classifier.hpp:148
virtual bool test_confusion(std::vector< RETURNTYPE > &confusion, DIMTYPE &classes, const PDataAccessor da)
Definition classifier.hpp:181
virtual bool test(RETURNTYPE &result, const PDataAccessor da)
Definition classifier.hpp:153
virtual bool test_scores_matrix(std::vector< RETURNTYPE > &scores, std::vector< DIMTYPE > &labels, DIMTYPE &classes, const PDataAccessor da)
Definition classifier.hpp:238
std::shared_ptr< SUBSET > const PSubset
shared pointer to the (fixed, non-reassignable) feature subset being evaluated
Definition classifier.hpp:100
virtual bool train(const PDataAccessor da, const PSubset sub)=0
learns from designated training part of data
DATAACCESSOR::PPattern PPattern
one data sample as exposed by DATAACCESSOR (a raw pointer to its feature vector)
Definition classifier.hpp:101
virtual bool test_scores(std::vector< std::pair< RETURNTYPE, DIMTYPE > > &scores, const PDataAccessor da)
Definition classifier.hpp:212
FST4 specific error passing class.
Definition error.hpp:61
Defines interface for classes that may need to get cloned.
FST4 specific error passing class.
Global definitions.
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
void notify(const std::string msg)
Prints msg followed by a newline via syncout::print(); a no-op unless the preprocessor macro DEBUG is...
Definition global.hpp:112
@ TEST
the split's test part
Definition data_accessor.hpp:86
@ auc
classification: two-class data = area under the ROC curve (exact rank statistic, midrank ties); K>2 =...
Definition wrapper_metrics.hpp:65
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
Wrapper criterion metrics beyond accuracy (balanced accuracy, F1, AUC, class weights,...
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.