Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
model_normal.hpp
Go to the documentation of this file.
1#ifndef FSTMODELNORMAL_H
2#define FSTMODELNORMAL_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 <cassert>
51#include <iostream>
52#include <sstream>
53#include <limits>
54#include <cmath>
55#include <cstring> // memcpy
56#include "error.hpp"
57#include "global.hpp"
58#include "model.hpp"
59#include "indexed_vector.hpp"
60#include "indexed_matrix.hpp"
61
62/*============== Template parameter type naming conventions ==============
63--------- Numeric types: -------------------------------------------------
64DATATYPE - data sample values - usually real numbers (but may be integers
65 in text processing etc.)
66REALTYPE - must be real numbers - for representing intermediate results of
67 calculations like mean, covariance etc.
68IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
69 extent depends on numbers of samples in data
70DIMTYPE - index values for enumeration of features (dimensions), or classes (not
71 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
72 but be aware of expressions like _classes*_features*_features !
73 in linearized representations of feature matrices for all classes
74BINTYPE - feature selection marker type - represents ca. <10 different feature
75 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
76RETURNTYPE - criterion value: real value, but may be extended in future to support
77 multiple values
78--------- Class types: ---------------------------------------------------
79SUBSET - class of class type Subset
80CLASSIFIER - class implementing interface defined in abstract class Classifier
81EVALUATOR - class implementing interface defined in abstract class Sequential_Step
82DISTANCE - class implementing interface defined in abstract class Distance
83DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
84INTERVALCONTAINER - class of class type Data_Intervaller
85CONTAINER - STL container of class type Data_Interval
86========================================================================== */
87
88namespace FST {
89
111template<typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
112class Model_Normal : public Model<SUBSET,DATAACCESSOR> {
113public:
114 typedef std::shared_ptr<DATAACCESSOR> PDataAccessor;
115 typedef std::shared_ptr<SUBSET> PSubset;
116 Model_Normal() : _classes(0), _n_max(0), _n(0), _d(0) {notify("Model_Normal (empty) constructor");}
117 Model_Normal(const Model_Normal& mn);
118 virtual ~Model_Normal() {notify("Model_Normal destructor");}
119
120 virtual void learn(PDataAccessor da) override; // learn model of full dimensionality
125 virtual void learn(PDataAccessor da, const PSubset sub);
126
127 // NOTE: the learn() methods create model structures. narrow() methods allow access to sub-space in existing learned structures
128 void narrow_to(const PSubset sub) override; // fill _index[] -> access to data is then mapped to sub-matrix
129 void denarrow() override; // reset _index[] -> access is reset to the full matrix
130
131 DIMTYPE get_n_max() const {return _n_max;}
132 DIMTYPE get_n() const {return _n;}
133 DIMTYPE get_d() const {return _d;}
134 DIMTYPE get_classes() const {return _classes;}
135
138 REALTYPE get_Pc(const DIMTYPE c) const {assert(c<_classes); return _Pc[c];}
139protected:
140 template<class, class, class, class, class, class, class> friend class Classifier_Normal_Bayes;
141 template<class, class, class, class, class, class, class> friend class Classifier_LDA; // pooled-covariance discriminant, pools the learned per-class moments
142 template<class, class, class, class, class, class, class> friend class Criterion_Normal; // owns the shared _meandif computation for all normal-model criteria
143 template<class, class, class, class, class, class, class> friend class Criterion_Normal_Bhattacharyya;
144 template<class, class, class, class, class, class, class> friend class Criterion_Normal_GMahalanobis;
145 template<class, class, class, class, class, class, class> friend class Criterion_Normal_Divergence;
146 const std::unique_ptr<Indexed_Vector<REALTYPE,DIMTYPE,SUBSET>[]>& get_mean() const {return _mean;}
147 const std::unique_ptr<Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET>[]>& get_cov() const {return _cov;}
148protected:
149 DIMTYPE _classes;
150 // NOTE: matrix size is defined by:
151 // _n_max - hard constant representing maximum allocated space of size _n_max*_n_max
152 // _n - "full feature set" dimensionality, _n<=_n_max. If Indexed_Matrix servers as output buffer
153 // to store matrices of various (reduced) dimensionalites, this is needed for correct functionality
154 // of at(), at_raw() etc. (both "raw" and "non-raw" matrix item access methods)
155 // _d - "virtual subset" dimensionality, _d<=_n. Assuming the matrix holds _n*_n values (_n consecutively stored
156 // rows of _n values each), a virtual sub-matrix can be defined and accessed using at() ("non-raw" methods)
157 DIMTYPE _n_max;
158 DIMTYPE _n;
159 std::unique_ptr<REALTYPE[]> _Pc;
160 std::unique_ptr<Indexed_Vector<REALTYPE,DIMTYPE,SUBSET>[]> _mean;
161 std::unique_ptr<Indexed_Matrix<REALTYPE,DIMTYPE,SUBSET>[]> _cov;
162 // NOTE: every cov[] and mean[] carries its own copy of the same subspace index map, so a
163 // narrow_to() has to walk all 2*_classes of them. It is a deliberate trade: the vectors and
164 // matrices stay self-contained and usable on their own.
165 DIMTYPE _d;
166 // NOTE: _n and _d must always stay synchronized with those inside _mean and _cov - nothing
167 // enforces it, so every method that changes one must change all of them (see learn(),
168 // narrow_to(), denarrow())
169
173 std::unique_ptr<DIMTYPE[]> _learn_index;
174};
175
176template<typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
177Model_Normal<DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::Model_Normal(const Model_Normal& mn) : // copy-constructor
178 Model<SUBSET,DATAACCESSOR>(mn),
179 _classes(mn._classes),
180 _n_max(mn._n_max),
181 _n(mn._n),
182 _d(mn._d)
183{
184 notify("Model_Normal copy-constructor.");
185 if(_classes>0) {
186 _Pc.reset(new REALTYPE[_classes]); memcpy((void *)_Pc.get(),(void *)(mn._Pc).get(),sizeof(REALTYPE)*_classes);
187 // default-constructed, then assigned one by one below: the array forms cannot pass
188 // constructor arguments, and the assignment operators do the deep copy anyway
191 for(DIMTYPE c=0;c<_classes;c++) {_mean[c]=mn._mean[c]; _cov[c]=mn._cov[c];}
192 }
193 if(_n_max>0)
194 {
195 // to store subspace info for the to-be-learned model
196 _learn_index.reset(new DIMTYPE[_n_max]); memcpy((void *)_learn_index.get(),(void *)(mn._learn_index).get(),sizeof(DIMTYPE)*_n_max);
197 }
198}
199
200template<typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
202{
203 assert(da);
204 assert(da->getNoOfClasses()>0);
205 assert(da->getNoOfFeatures()>0);
206 PSubset fullset(new SUBSET(da->getNoOfFeatures()));
207 fullset->select_all();
208#ifdef DEBUG
209 {std::ostringstream sos; sos << "fullset = "<< *fullset << std::endl; syncout::print(std::cout,sos);}
210#endif
211 learn(da,fullset);
212}
213
214template<typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
216{
217 notify("Model_Normal::learn():");
218 assert(da->getNoOfClasses()>0);
219 assert(da->getNoOfFeatures()>0);
220 assert(sub);
221 assert(sub->get_n_raw()==da->getNoOfFeatures());
222 assert(sub->get_d_raw()>0);
223
224 if(_classes!=da->getNoOfClasses())
225 {
226 _classes=da->getNoOfClasses();
229 _Pc.reset(new REALTYPE[_classes]);
230 // the fresh vectors/matrices have no storage yet; zeroing the recorded capacity makes
231 // the "insufficient buffers" test below fire unconditionally and size them
232 _n_max=0;
233 }
234 // NOTE: the class priors are re-estimated on every learn() call, even when da has not changed.
235 // It is a negligible cost next to the covariance accumulation and keeps learn() free of
236 // any assumption about what the caller changed since the last call.
237 // NOTE: class priors are estimated from complete data available through da.
238 // Note that when only parts of data are processed (i.e., using data splits), the
239 // estimates below would possibly not precisely depict the relative frequency of documents
240 // in stratified class parts, esp. if class sizes are small
241 IDXTYPE _allpatterns=da->getClassSizeSum(); // just to estimate class priors
242 assert(_allpatterns>0);
243 for(DIMTYPE c=0;c<_classes;c++) _Pc[c]=(REALTYPE)da->getClassSize(c)/(REALTYPE)_allpatterns;
244
245 if(sub->get_n_raw()>_n_max) // insufficient buffers - reset constants and reallocate
246 {
247 _n_max=sub->get_n_raw();
248 for(DIMTYPE c=0;c<_classes;c++) {
249 _mean[c].reset(_n_max);
250 _cov[c].reset(_n_max);
251 }
252 _learn_index.reset(new DIMTYPE[_n_max]); // to store subspace info for the to-be-learned model
253 }
254
255 _n=sub->get_d_raw(); // actual to-be-learned model dimensionality is to be the one defined by the subset defined in sub
256 for(DIMTYPE c=0;c<_classes;c++) {
257 _mean[c].redim(_n); _mean[c].set_all_raw_to(0.0);
258 _cov[c].redim(_n); _cov[c].set_all_raw_to(0.0);
259 }
260 _d=_n; // access to learned model is initially not narrow()ed
261 DIMTYPE fi=0;
262 for(DIMTYPE f : sub->selected_features()) {_learn_index[fi++]=f;}
263 assert(fi==_n);
264
265 typename DATAACCESSOR::PPattern p;
266 IDXTYPE cnt;
267 // pattern rows are laid out with the CURRENT dataset's raw feature count as stride. Note
268 // that this is sub->get_n_raw() and NOT _n_max: the latter only grows, so after the model
269 // has been reused on a smaller dataset the two differ and _n_max-strided reads would walk
270 // off the row boundaries.
271 const DIMTYPE _stride=sub->get_n_raw();
272 assert(da->getSplitIndex()>0); // splitting loop to be maintained externally
273 // mean
274 for(DIMTYPE c=0;c<_classes;c++) {
275 da->setClass(c);
276 assert(da->getNoOfPatterns(TRAIN)>0);
277#ifdef DEBUG
278 {std::ostringstream sos; sos << "da->getNoOfPatterns(TRAIN) [class="<<c<<"] = "<<da->getNoOfPatterns(TRAIN)<<std::endl; syncout::print(std::cout,sos);}
279#endif
280 for(const auto &blk : da->blocks(TRAIN)) { p=blk.data; cnt=blk.size;
281 for(IDXTYPE i=0;i<cnt;i++)
282 for(DIMTYPE f=0;f<_n;f++) (_mean[c])[f]+=p[(std::size_t)i*_stride+_learn_index[f]];
283 }
284 for(DIMTYPE f=0;f<_n;f++) (_mean[c])[f]/=da->getNoOfPatterns(TRAIN);
285 }
286
287#ifdef DEBUG
288 {
289 std::ostringstream sos; sos << "_Pc:"<<std::endl;
290 for(DIMTYPE c=0;c<_classes;c++) sos << _Pc[c] << " ";
291 sos << std::endl;
292 sos << "_mean:"<<std::endl;
293 for(DIMTYPE c=0;c<_classes;c++) {for(DIMTYPE f=0;f<_n;f++) sos << (_mean[c])[f] << " "; sos << std::endl;}
294 syncout::print(std::cout,sos);
295 }
296#endif
297 // cov
298 for(DIMTYPE c=0;c<_classes;c++) {
299 da->setClass(c);
300 for(const auto &blk : da->blocks(TRAIN)) { p=blk.data; cnt=blk.size;
301 for(IDXTYPE i=0;i<cnt;i++) {
302 for(DIMTYPE f1=0;f1<_n;f1++)
303 for(DIMTYPE f2=f1;f2<_n;f2++){
304 _cov[c].at_raw(f1,f2)+=(p[(std::size_t)i*_stride+_learn_index[f1]]-(_mean[c])[f1])*(p[(std::size_t)i*_stride+_learn_index[f2]]-(_mean[c])[f2]);
305 }
306 }
307 }
308 }
309 // normalize and copy to lower triangle
310 // ORDER MATTERS: the upper triangle is mirrored into the lower one FIRST and the division
311 // by the pattern count is applied to the whole matrix afterwards. Dividing first and
312 // mirroring second would work too, but mirroring after the division of only one triangle
313 // would not - do not reorder these two loops.
314 for(DIMTYPE c=0;c<_classes;c++) {
315 da->setClass(c);
316 // the unbiased (n-1) divisor. A class with a single training pattern would make it 0:
317 // the assert catches that in a debug build, the clamp keeps a release build from
318 // dividing by zero (the covariance is meaningless either way).
319 REALTYPE pats=da->getNoOfPatterns(TRAIN)-1;
320 assert(pats>0);
321 if(pats<1) pats=1;
322 for(DIMTYPE f1=0;f1<_n;f1++)
323 for(DIMTYPE f2=0;f2<f1;f2++) {
324 _cov[c].at_raw(f1,f2)=_cov[c].at_raw(f2,f1);
325 }
326 for(DIMTYPE f1=0;f1<_n;f1++)
327 for(DIMTYPE f2=0;f2<_n;f2++) {
328 _cov[c].at_raw(f1,f2)/=pats;
329 }
330 }
331
332
333#ifdef DEBUG
334 {
335 std::ostringstream sos; sos << "_cov:"<<std::endl;
336 for(DIMTYPE c=0;c<_classes;c++) {for(DIMTYPE f1=0;f1<_n;f1++) {for(DIMTYPE f2=0;f2<_n;f2++) sos << _cov[c].at_raw(f1,f2) << " "; sos << std::endl;} sos << std::endl;}
337 syncout::print(std::cout,sos);
338 }
339#endif
340}
341
342
343template<typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
345{
346 assert(_mean);
347 assert(_cov);
348 assert(sub->get_n_raw()==_n); // NOTE: sub should represent subspace of the learned subspace ! not of the full space
349 assert(sub->get_d_raw()>0);
350 for(DIMTYPE c=0;c<_classes;c++) {
351 _mean[c].narrow_to(sub);
352 _cov[c].narrow_to(sub);
353 }
354 _d=sub->get_d_raw();
355}
356
357template<typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
359{
360 assert(_mean);
361 assert(_cov);
362 for(DIMTYPE c=0;c<_classes;c++) {
363 _mean[c].denarrow();
364 _cov[c].denarrow();
365 }
366 _d=_n;
367}
368
369} // namespace
370#endif // FSTMODELNORMAL_H ///:~
Linear (Fisher/Gaussian shared-covariance) discriminant classifier.
Definition classifier_lda.hpp:111
Implements Bayes classifier based on normal (gaussian) model.
Definition classifier_normal_bayes.hpp:116
Implements Bhattacharyya distance based on normal (gaussian) model to serve as feature selection crit...
Definition criterion_normal_bhattacharyya.hpp:105
Implements Divergence (distance) based on normal (gaussian) model to serve as feature selection crite...
Definition criterion_normal_divergence.hpp:110
Implements Generalized Mahalanobis distance based on normal (gaussian) model to serve as feature sele...
Definition criterion_normal_gmahalanobis.hpp:107
Abstract class, defines interface for feature selection criterion implementations based on normal mod...
Definition criterion_normal.hpp:100
Matrix representation and operations, allows operation in selected subspace.
Definition indexed_matrix.hpp:102
Vector representation and operations, allows operation in selected subspace. Also serves as the helpe...
Definition indexed_vector.hpp:100
Implements normal (gaussian) model.
Definition model_normal.hpp:112
DIMTYPE get_d() const
the current virtual (narrow_to()'d) dimensionality, get_d()<=get_n()
Definition model_normal.hpp:133
std::unique_ptr< Indexed_Vector< REALTYPE, DIMTYPE, SUBSET >[]> _mean
per-class learned mean vectors, get_classes() entries; see get_mean()
Definition model_normal.hpp:160
DIMTYPE _d
current virtual (narrow_to()'d) dimensionality, _d<=_n; see get_d()
Definition model_normal.hpp:165
const std::unique_ptr< Indexed_Matrix< REALTYPE, DIMTYPE, SUBSET >[]> & get_cov() const
per-class learned covariance matrices (array of get_classes() Indexed_Matrix, each get_n()/get_d() si...
Definition model_normal.hpp:147
REALTYPE get_Pc(const DIMTYPE c) const
Definition model_normal.hpp:138
std::unique_ptr< Indexed_Matrix< REALTYPE, DIMTYPE, SUBSET >[]> _cov
per-class learned covariance matrices, get_classes() entries; see get_cov()
Definition model_normal.hpp:161
std::unique_ptr< REALTYPE[]> _Pc
per-class prior probabilities P(c), get_classes() entries; see get_Pc()
Definition model_normal.hpp:159
DIMTYPE _n_max
allocated capacity: the largest sub->get_n_raw() (the subset's full allocation size,...
Definition model_normal.hpp:157
void narrow_to(const PSubset sub) override
Definition model_normal.hpp:344
DIMTYPE get_n_max() const
the allocated capacity (max n learn() can grow to without reallocating)
Definition model_normal.hpp:131
DIMTYPE get_classes() const
number of classes the model was learned over (learn()'s da->getNoOfClasses())
Definition model_normal.hpp:134
const std::unique_ptr< Indexed_Vector< REALTYPE, DIMTYPE, SUBSET >[]> & get_mean() const
per-class learned mean vectors (array of get_classes() Indexed_Vector, each of dimensionality get_n()...
Definition model_normal.hpp:146
virtual void learn(PDataAccessor da) override
Fits the model's parameters from da's training data, over all its features. Concrete models may addit...
Definition model_normal.hpp:201
DIMTYPE _classes
number of classes the model is learned over; see get_classes() – NOTE: expressions like _classes*_n*_...
Definition model_normal.hpp:149
void denarrow() override
Undoes narrow_to(): model access reverts to the full dimensionality it was learned at.
Definition model_normal.hpp:358
std::unique_ptr< DIMTYPE[]> _learn_index
Definition model_normal.hpp:173
DIMTYPE _n
full ("raw") dimensionality the model is currently learned at, _n<=_n_max; see get_n() – NOTE: expres...
Definition model_normal.hpp:158
std::shared_ptr< SUBSET > PSubset
shared pointer to a feature subset, as consumed by learn(da,sub)/narrow_to()
Definition model_normal.hpp:115
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying training data
Definition model_normal.hpp:114
DIMTYPE get_n() const
the full ("raw") dimensionality the model is currently learned at, get_n()<=get_n_max()
Definition model_normal.hpp:132
static void print(std::ostream &os, std::ostringstream &sos)
Writes sos's buffered content to os, serializing concurrent writers under FST_THREADED via a shared m...
Definition global.hpp:86
FST4 specific error passing class.
Global definitions.
Matrix representation and operations, allows operation in selected subspace.
Vector representation and operations, allows operation in selected subspace.
Defines interface for data model implementations.
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
@ TRAIN
the split's training part
Definition data_accessor.hpp:85
@ 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.