Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
classifier_lda.hpp
Go to the documentation of this file.
1#ifndef FSTCLASSIFIERLDA_H
2#define FSTCLASSIFIERLDA_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 <memory>
48#include <vector>
49#include <cmath>
50#include <cstring>
51#include <cassert>
52#include <iostream>
53#include "error.hpp"
54#include "global.hpp"
55#include "clonable.hpp"
56#include "classifier.hpp"
57#include "model_normal.hpp"
58#include "indexed_matrix.hpp"
59
60/*============== Template parameter type naming conventions ==============
61--------- Numeric types: -------------------------------------------------
62DATATYPE - data sample values - usually real numbers (but may be integers
63 in text processing etc.)
64REALTYPE - must be real numbers - for representing intermediate results of
65 calculations like mean, covariance etc.
66IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
67 extent depends on numbers of samples in data
68DIMTYPE - index values for enumeration of features (dimensions), or classes (not
69 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
70 but be aware of expressions like _classes*_features*_features !
71 in linearized representations of feature matrices for all classes
72BINTYPE - feature selection marker type - represents ca. <10 different feature
73 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
74RETURNTYPE - criterion value: real value, but may be extended in future to support
75 multiple values
76--------- Class types: ---------------------------------------------------
77SUBSET - class of class type Subset
78CLASSIFIER - class implementing interface defined in abstract class Classifier
79EVALUATOR - class implementing interface defined in abstract class Sequential_Step
80DISTANCE - class implementing interface defined in abstract class Distance
81DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
82INTERVALCONTAINER - class of class type Data_Intervaller
83CONTAINER - STL container of class type Data_Interval
84========================================================================== */
85
86namespace FST {
87
110template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
111class Classifier_LDA : public Clonable_Impl<Classifier_LDA<RETURNTYPE,DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>,Classifier<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR> > {
112public:
115 typedef std::shared_ptr<DATAACCESSOR> PDataAccessor;
116 typedef std::shared_ptr<SUBSET> PSubset;
117 typedef typename DATAACCESSOR::PPattern PPattern;
118 Classifier_LDA()
119 {
120 notify("Classifier_LDA constructor.");
123 }
124 virtual ~Classifier_LDA() {notify("Classifier_LDA destructor.");}
125
128 void initialize(const PDataAccessor da);
129
130 virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override; // argmax of the linear discriminants; first maximum wins
131 virtual bool classify_scores(std::vector<RETURNTYPE> &scores, const PPattern &pattern) override; // per-class discriminant values for ranking metrics (AUC)
140 virtual bool train(const PDataAccessor da, const PSubset sub) override; // learns per-class model, pools covariance, inverts once
141 // test() = the canonical accuracy loop inherited from Classifier, driven by classify() above
142
143 virtual std::ostream& print(std::ostream& os) const override {os << "Classifier_LDA()"; return os;}
144private:
145 friend parent_impl;
148 Classifier_LDA(const Classifier_LDA& cld) :
149 _classes(cld._classes),
150 _features(cld._features),
152 _index(cld._index),
153 _a(cld._a),
154 _b(cld._b),
155 _m(cld._m)
156 {
157 notify("Classifier_LDA copy-constructor.");
162 }
163protected:
164 std::unique_ptr<Model_Normal<DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR> > _model;
165 std::unique_ptr<Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET> > _LUtemp;
166 std::unique_ptr<Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET> > _pooled;
167 std::unique_ptr<Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET> > _pooledinv;
168
169 DIMTYPE _classes;
170 DIMTYPE _features;
171 DIMTYPE _subfeatures;
172 std::vector<DIMTYPE> _index;
173 std::vector<RETURNTYPE> _cs_sc;
174 std::vector<REALTYPE> _a;
175 std::vector<REALTYPE> _b;
176 std::vector<REALTYPE> _m;
177};
178
179template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
181{
182 notify("Classifier_LDA::initialize().");
183 assert(da);
184 assert(da->getNoOfFeatures()>0);
185 if(da->getNoOfClasses()<2) throw fst_error("Classifier_LDA requires at least 2 classes");
186 if(!_LUtemp) _LUtemp.reset(new Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET>(da->getNoOfFeatures(),true));
187 if(da->getNoOfFeatures()>_LUtemp->get_n_max()) _LUtemp->reset(da->getNoOfFeatures(),true);
188 if(!_pooled) _pooled.reset(new Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET>(da->getNoOfFeatures(),false));
189 if(da->getNoOfFeatures()>_pooled->get_n_max()) _pooled->reset(da->getNoOfFeatures(),false);
190 if(!_pooledinv) _pooledinv.reset(new Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET>(da->getNoOfFeatures(),false));
191 if(da->getNoOfFeatures()>_pooledinv->get_n_max()) _pooledinv->reset(da->getNoOfFeatures(),false);
192 _classes=da->getNoOfClasses();
193 _features=da->getNoOfFeatures();
194 _index.resize(_features);
195 _subfeatures=0; // no model yet
196}
197
198template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
200{
201 notify("Classifier_LDA::train().");
202 assert(da);
203 assert(sub);
204 assert(_model);
205 assert(_classes==da->getNoOfClasses());
206 assert(_features==da->getNoOfFeatures());
207 assert(sub->get_n_raw()==da->getNoOfFeatures());
208
209 // buffer the feature subset index
210 _subfeatures=0;
211 for(DIMTYPE fsel : sub->selected_features()) {assert(_subfeatures<_features); _index[_subfeatures++]=fsel;}
212 assert(_subfeatures==sub->get_d_raw());
213 assert(_subfeatures>0);
214 const DIMTYPE d=_subfeatures;
215
216 // per-class means/covariances on the designated training part (subset dimensionality)
217 _model->learn(da,sub);
218 _model->denarrow();
219
220 // pooled within-class covariance: class covariances weighted by the priors
221 _pooled->redim(d);
222 for(DIMTYPE f1=0;f1<d;f1++)
223 for(DIMTYPE f2=0;f2<d;f2++)
224 {
225 REALTYPE v=0.0;
226 for(DIMTYPE c=0;c<_classes;c++) v+=_model->get_Pc(c)*_model->get_cov()[c].at_raw(f1,f2);
227 _pooled->at_raw(f1,f2)=v;
228 }
229
230 // single inverse through the shared Indexed_Matrix LU machinery (Eigen-path parity)
231 _pooledinv->redim(d);
232 _LUtemp->redim(d);
233 _pooled->LUdecompose(*_LUtemp);
234 _pooled->invert(*_pooledinv,*_LUtemp);
235
236 // closed-form discriminants in CENTERED form. With m the prior-weighted mean of the class
237 // means and delta_c = mu_c - m, the LDA score mu_c'S^-1 x - 1/2 mu_c'S^-1 mu_c + ln P(c)
238 // equals delta_c'S^-1 (x-m) - 1/2 delta_c'S^-1 delta_c + ln P(c) plus a term that does not
239 // depend on the class, so a_c = S^-1 delta_c and b_c = -1/2 delta_c.a_c + ln P(c), applied to
240 // x-m, yield the same decisions and the same per-sample score differences. The uncentered
241 // form builds O(1) class differences out of O(|mean|^2/var) terms and loses digits as soon
242 // as the features carry a large common offset (with an offset of 1e8 it dropped from 0.73 to
243 // 0.60 accuracy on data the centered form classifies exactly as without the offset).
244 _m.assign(d,0.0);
245 for(DIMTYPE c=0;c<_classes;c++)
246 {
247 const auto &mu=_model->get_mean()[c];
248 const REALTYPE pc=_model->get_Pc(c);
249 for(DIMTYPE f1=0;f1<d;f1++) _m[f1]+=pc*mu.at_raw(f1); // the priors sum to 1
250 }
251 _a.assign((std::size_t)_classes*d,0.0);
252 _b.assign(_classes,0.0);
253 for(DIMTYPE c=0;c<_classes;c++)
254 {
255 REALTYPE *ac=&_a[(std::size_t)c*d];
256 auto &mu=_model->get_mean()[c];
257 for(DIMTYPE f1=0;f1<d;f1++)
258 {
259 REALTYPE v=0.0;
260 for(DIMTYPE f2=0;f2<d;f2++) v+=_pooledinv->at_raw(f1,f2)*(mu.at_raw(f2)-_m[f2]);
261 ac[f1]=v;
262 }
263 REALTYPE dot=0.0;
264 for(DIMTYPE f1=0;f1<d;f1++) dot+=ac[f1]*(mu.at_raw(f1)-_m[f1]);
265 _b[c]=-0.5*dot+std::log(_model->get_Pc(c));
266 }
267 // singular pooled covariance produces non-finite coefficients — fail loudly like the normal-model filters
268 for(std::size_t j=0;j<_a.size();j++) if(!std::isfinite(_a[j])) return false;
269 for(DIMTYPE c=0;c<_classes;c++) if(!std::isfinite(_b[c])) return false;
270 return true;
271}
272
273template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
275{
276 assert(_subfeatures>0); // train() must have succeeded
277 const DIMTYPE d=_subfeatures;
278 scores.resize(_classes);
279 for(DIMTYPE c=0;c<_classes;c++)
280 {
281 const REALTYPE *ac=&_a[(std::size_t)c*d];
282 REALTYPE v=_b[c];
283 for(DIMTYPE f=0;f<d;f++) v+=ac[f]*((REALTYPE)pattern[_index[f]]-_m[f]); // centered, see train()
284 scores[c]=(RETURNTYPE)v;
285 }
286 return true;
287}
288
289template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
291{
292 if(!classify_scores(_cs_sc,pattern)) return false; // member scratch: avoids a per-pattern allocation
293 cls=0;
294 for(DIMTYPE c=1;c<_classes;c++) if(_cs_sc[c]>_cs_sc[cls]) cls=c; // first maximum wins
295 return true;
296}
297
298
299} // namespace
300#endif // FSTCLASSIFIERLDA_H ///:~
Linear (Fisher/Gaussian shared-covariance) discriminant classifier.
Definition classifier_lda.hpp:111
virtual bool train(const PDataAccessor da, const PSubset sub) override
Learns per-class Gaussian parameters on the designated training part, pools the per-class covariances...
Definition classifier_lda.hpp:199
std::unique_ptr< Model_Normal< DATATYPE, REALTYPE, IDXTYPE, DIMTYPE, SUBSET, DATAACCESSOR > > _model
per-class means/covariances (learned by train(), see Model_Normal)
Definition classifier_lda.hpp:164
DIMTYPE _subfeatures
d = current subset size
Definition classifier_lda.hpp:171
std::unique_ptr< Indexed_Matrix< REALTYPE, DIMTYPE, SUBSET > > _pooled
pooled within-class covariance
Definition classifier_lda.hpp:166
virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override
Definition classifier_lda.hpp:290
DIMTYPE _features
raw (full) feature count, as recorded by initialize()
Definition classifier_lda.hpp:170
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the object; base default writes nothing
Definition classifier_lda.hpp:143
std::unique_ptr< Indexed_Matrix< REALTYPE, DIMTYPE, SUBSET > > _pooledinv
its inverse
Definition classifier_lda.hpp:167
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying train/test blocks
Definition classifier_lda.hpp:115
std::vector< DIMTYPE > _index
subset position -> raw feature index (first d entries valid)
Definition classifier_lda.hpp:172
std::unique_ptr< Indexed_Matrix< REALTYPE, DIMTYPE, SUBSET > > _LUtemp
LU scratch (lazy allocation).
Definition classifier_lda.hpp:165
std::vector< RETURNTYPE > _cs_sc
classify() score scratch (member to avoid per-pattern allocation)
Definition classifier_lda.hpp:173
Classifier_LDA(const Classifier_LDA &cld)
Deep-copies the learned model and the pooled-covariance matrices (LU scratch, pooled covariance,...
Definition classifier_lda.hpp:148
DATAACCESSOR::PPattern PPattern
one data sample as exposed by DATAACCESSOR (a raw pointer to its feature vector)
Definition classifier_lda.hpp:117
std::vector< REALTYPE > _a
classes x d discriminant directions, row-major
Definition classifier_lda.hpp:174
virtual bool classify_scores(std::vector< RETURNTYPE > &scores, const PPattern &pattern) override
Definition classifier_lda.hpp:274
DIMTYPE _classes
number of classes, as recorded by initialize()
Definition classifier_lda.hpp:169
std::vector< REALTYPE > _b
classes discriminant offsets
Definition classifier_lda.hpp:175
Classifier< RETURNTYPE, DIMTYPE, SUBSET, DATAACCESSOR > parent
the abstract base type
Definition classifier_lda.hpp:113
void initialize(const PDataAccessor da)
Pre-allocates model and matrix buffers and records the class/feature counts of da....
Definition classifier_lda.hpp:180
Clonable_Impl< Classifier_LDA, parent > parent_impl
implements clone() via the copy-constructor below
Definition classifier_lda.hpp:114
std::vector< REALTYPE > _m
d-entry centering point of the discriminants: the prior-weighted mean of the class means (see train()...
Definition classifier_lda.hpp:176
std::shared_ptr< SUBSET > PSubset
shared pointer to the feature subset being trained/classified on
Definition classifier_lda.hpp:116
abstract class, defines interface for classifier implementations (mainly to be used in wrappers)
Definition classifier.hpp:97
CRTP mixin implementing the Clonable protocol for a concrete class.
Definition clonable.hpp:184
Matrix representation and operations, allows operation in selected subspace.
Definition indexed_matrix.hpp:102
Implements normal (gaussian) model.
Definition model_normal.hpp:112
FST4 specific error passing class.
Definition error.hpp:61
Defines interface for classifier implementations.
Defines interface for classes that may need to get cloned.
FST4 specific error passing class.
Global definitions.
Matrix representation and operations, allows operation in selected subspace.
Implements normal (gaussian) model.
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
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
@ f1
classification, two-class data: F1 of the positive class, which is Metric_Spec::f1_positive (class in...
Definition wrapper_metrics.hpp:63
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.