Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
classifier_multinom_naivebayes.hpp
Go to the documentation of this file.
1#ifndef FSTCLASSIFIERMULTINOMNAIVEBAYES_H
2#define FSTCLASSIFIERMULTINOMNAIVEBAYES_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 <cmath>
54#include <cstring> // memcpy
55#include "error.hpp"
56#include "global.hpp"
57#include "classifier.hpp"
58#include "model_multinom.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
112template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
113class Classifier_Multinomial_NaiveBayes : public Clonable_Impl<Classifier_Multinomial_NaiveBayes<RETURNTYPE,DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>,Classifier<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR> > {
114public:
116 typedef std::shared_ptr<DATAACCESSOR> PDataAccessor;
117 typedef std::shared_ptr<SUBSET> const PSubset;
118 typedef typename DATAACCESSOR::PPattern PPattern;
119 Classifier_Multinomial_NaiveBayes();
120 virtual ~Classifier_Multinomial_NaiveBayes() {notify("Classifier_Multinomial_NaiveBayes destructor.");}
121
127 void enable_prelearn_mode(const PDataAccessor da);
129 bool get_prelearn_mode() const {return _prelearn_mode;}
130
136 void initialize(const PDataAccessor da);
137
138 virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override; // classifies pattern, returns the respective class index
139 virtual bool classify_scores(std::vector<RETURNTYPE> &scores, const PPattern &pattern) override
140 { // per-class log-posteriors for ranking metrics (AUC); single features are unusable
141 // (theta=1 in each class, see classify()) -> equal scores = uninformative ranking (AUC 0.5)
142 if(_subfeatures==1) {scores.assign(_classes,(RETURNTYPE)0.0); return true;}
143 DIMTYPE cls;
144 if(!classify(cls,pattern)) return false;
145 scores.resize(_classes);
146 for(DIMTYPE c=0;c<_classes;c++) scores[c]=(RETURNTYPE)_Pcd[c];
147 return true;
148 }
149 virtual bool test_confusion(std::vector<RETURNTYPE> &confusion, DIMTYPE &classes, const PDataAccessor da) override
150 { // mirrors test()'s "single features unusable" convention (correct=0, count=1):
151 // one off-diagonal count -> every confusion metric scores 0, like accuracy does
152 if(_subfeatures==1) {
153 classes=da->getNoOfClasses();
154 if(classes<2) return false;
155 confusion.assign((size_t)classes*(size_t)classes,(RETURNTYPE)0.0);
156 confusion[1]=1.0; // true class 0 predicted as class 1
157 return true;
158 }
160 }
161 virtual bool train(const PDataAccessor da, const PSubset sub) override; // learns from designated training part of data
162 virtual bool test(RETURNTYPE &result, const PDataAccessor da) override; // estimates accuracy using designated test data
163
164 DIMTYPE get_n() const {assert(_model); return _model->get_n();}
165 DIMTYPE get_d() const {assert(_model); return _model->get_d();}
166
167
168
169 virtual std::ostream& print(std::ostream& os) const override {os << "Classifier_Multinomial_NaiveBayes()"; return os;}
170private:
171 friend parent_impl;
172 Classifier_Multinomial_NaiveBayes(const Classifier_Multinomial_NaiveBayes& cmnb);
173protected:
174 std::unique_ptr<Model_Multinomial<DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR> > _model;
175
176 DIMTYPE _classes;
177 DIMTYPE _features;
178 std::unique_ptr<REALTYPE[]> _Pcd;
179
180private:
182 std::unique_ptr<DIMTYPE[]> _index;
183 DIMTYPE _subfeatures;
184};
185
186template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
187Classifier_Multinomial_NaiveBayes<RETURNTYPE,DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::Classifier_Multinomial_NaiveBayes(const Classifier_Multinomial_NaiveBayes& cmnb) :
188 _classes(cmnb._classes),
189 _features(cmnb._features),
192{
193 notify("Classifier_Multinomial_NaiveBayes copy-constructor.");
195 if(_classes>0)
196 {
197 _Pcd.reset(new REALTYPE[_classes]); memcpy((void *)_Pcd.get(),(void *)(cmnb._Pcd).get(),sizeof(REALTYPE)*_classes);
198 }
199 if(_features>0) // guards against memcpy from cmnb._index==null when copying a never-initialize()d instance (_features>0 implies _index was allocated -- see initialize())
200 {
201 _index.reset(new DIMTYPE[_features]); memcpy((void *)_index.get(),(void *)(cmnb._index).get(),sizeof(DIMTYPE)*_features);
202 }
203}
204
205
206
207template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
208Classifier_Multinomial_NaiveBayes<RETURNTYPE,DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::Classifier_Multinomial_NaiveBayes()
209{
210 notify("Classifier_Multinomial_NaiveBayes constructor.");
212 _classes=0;
213 _features=0;
214 _subfeatures=0;
215 _prelearn_mode=false;
216}
217
218template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
220{
221 notify("Classifier_Multinomial_NaiveBayes::init_prelearn_mode().");
222 assert(_model);
223 assert(da);
224 assert(da->getNoOfFeatures()>0);
225 assert(da->getNoOfClasses()>0);
226 _model->learn(da);
227 _prelearn_mode=true;
228}
229
230template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
232{
233 notify("Classifier_Multinomial_NaiveBayes::initialize().");
234 assert(da);
235 assert(da->getNoOfFeatures()>0);
236 assert(da->getNoOfClasses()>0);
237
238 if(!_Pcd || _classes!=da->getNoOfClasses()) _Pcd.reset(new REALTYPE[da->getNoOfClasses()]);
239 _classes=da->getNoOfClasses();
240
241 if(!_index || _features!=da->getNoOfFeatures()) _index.reset(new DIMTYPE[da->getNoOfFeatures()]);
242 _features=da->getNoOfFeatures();
243}
244
245template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
247{
248 // NOTE: initialize() is called unconditionally on every train(), so the buffers always match
249 // the accessor currently in use even when the wrapper switches datasets or splits. It
250 // only (re)allocates when the class or feature count changed, so the repeated call is
251 // cheap; it could be skipped while evaluating several subsets over one unchanged split.
252 initialize(da);
253
254 notify("Classifier_Multinomial_NaiveBayes::train().");
255 assert(_model);
256 assert(da);
257 if(_prelearn_mode) assert(get_n()>0);
258 assert(da->getNoOfFeatures()>0);
259 assert(sub);
260 assert(sub->get_frozen_mode()==false);
261 if(_prelearn_mode) assert(sub->get_n_raw()==get_n());
262 assert(sub->get_n_raw()==da->getNoOfFeatures());
263
264 if(_prelearn_mode) _model->narrow_to(sub); // assume _n-dimensional model is pre-learned and needs to be narrow()ed only (NOTE: training data must not change since)
265 else {_model->learn(da,sub); _model->denarrow();} // re-learn from scratch, training data has changed (i.e., after switch to next data split)
266
267 // prepare feature subset index buffering
268 DIMTYPE f;
269 _subfeatures=0;
270 for(DIMTYPE fsel : sub->selected_features()) {assert(_subfeatures<get_n()); _index[_subfeatures++]=fsel;}
271
272 _model->compute_theta(); //thetas to be used in classify() and test()
273
274#ifdef DEBUG
275 {std::ostringstream sos; sos << "index: "; for(f=0;f<_subfeatures;f++) sos << _index[f] << " "; syncout::print(std::cout,sos);}
276#endif
277 assert(_subfeatures==sub->get_d_raw());
278 return true;
279}
280
281template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
283{
284 // NOTE: train() must have been called - it learns (or narrows) the model, fills the _index
285 // map and computes the model's theta buffer this method reads. \p pattern must point
286 // at a full, raw feature vector: it is indexed through _index, not pre-narrowed.
287 notify("Classifier_Multinomial_NaiveBayes::classify().");
288 assert(_model);
289 if(_prelearn_mode) assert(get_n()>0);
290 assert(_model->get_classes()>1);
291 assert(_classes==_model->get_classes());
292 assert(_index);
293 assert(_subfeatures==_model->get_d());
294 if(!_prelearn_mode) assert(_subfeatures==_model->get_n());
295 assert(_subfeatures>0);
296
297 DIMTYPE f;
298 DIMTYPE c_cand;
299 REALTYPE res;
300 DIMTYPE wTH;
301 // a non-const pointer into the model's theta buffer, only read from here; taking it is safe
302 // because this object owns _model exclusively and nothing reallocates the buffer during the
303 // loop below (the class-major stride is _subfeatures, the model's current get_d())
304 REALTYPE *theta=&(_model->get_theta()[0]);
305 REALTYPE *theta_tmp;
306
307 if(_subfeatures==1) {
308 // NOTE: single features are unusable for classification because theta=1 in each class,
309 // thus log(theta)=0 and consequently Pcd[] does not depend on feature frequency
310 cls=0; // consider all single features equally unusable
311 return false;
312 } else {
313 // compute P(c|pattern) for each class
314 wTH=0;
315 for(c_cand=0;c_cand<_classes;c_cand++)
316 {
317 theta_tmp=&theta[wTH];
318 res=0.0;
319 for(f=0;f<_subfeatures;f++)
320 {
321 res+=(REALTYPE)pattern[_index[f]] * log(theta_tmp[f]);
322 }
323 _Pcd[c_cand]=log(_model->get_Pc(c_cand))+res;
324 wTH+=_subfeatures;
325 }
326 // find maximum P[c|pattern]
327 res=_Pcd[0]; cls=0;
328 for(c_cand=1;c_cand<_classes;c_cand++) if(_Pcd[c_cand]>res) {res=_Pcd[c_cand]; cls=c_cand;}
329 }
330 return true;
331}
332
333template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
335{
336 // NOTE: train() must have been called for the subset being tested; the accessor must be
337 // positioned on the split whose TEST part is to be classified
338 notify("Classifier_Multinomial_NaiveBayes::test().");
339 assert(_model);
340 if(_prelearn_mode) assert(get_n()>0);
341 assert(_model->get_classes()>1);
342 assert(_classes==_model->get_classes());
343 assert(da);
344 if(_prelearn_mode) assert(da->getNoOfFeatures()==get_n());
345 assert(da->getNoOfClasses()>0);
346 assert(da->getNoOfClasses()==_classes);
347 assert(_index);
348 assert(_subfeatures==_model->get_d());
349 if(!_prelearn_mode) assert(_subfeatures==_model->get_n());
350 assert(_subfeatures>0);
351
352 typename DATAACCESSOR::PPattern p;
353 IDXTYPE s,i;
354 IDXTYPE count, correct;
355 DIMTYPE _features=da->getNoOfFeatures();
356 DIMTYPE clstmp;
357
358 if(_subfeatures==1) {
359 // NOTE: single features are unusable for classification because theta=1 in each class,
360 // thus log(theta)=0 and consequently Pcd[] does not depend on feature frequency
361 correct=0; count=1; // consider all single features equally unusable
362 } else {
363
364
365 count=0;
366 correct=0;
367 for(DIMTYPE c_test=0;c_test<_classes;c_test++)
368 {
369 da->setClass(c_test);
370 for(const auto &blk : da->blocks(TEST)) for(i=0,p=blk.data,s=blk.size;i<s;i++)
371 {
372 if(!classify(clstmp,&p[(std::size_t)i*_features])) return false;
373 if(clstmp==c_test) correct++;
374 count++;
375 }
376 }
377 }
378 assert(count>0);
379 result=(RETURNTYPE)correct/(RETURNTYPE)count;
380#ifdef DEBUG
381 {
382 std::ostringstream sos; sos << " result=" << result << std::endl;
383 syncout::print(std::cout,sos);
384 }
385#endif
386 return true;
387}
388
389} // namespace
390#endif // FSTCLASSIFIERMULTINOMNAIVEBAYES_H ///:~
std::unique_ptr< Model_Multinomial< DATATYPE, REALTYPE, IDXTYPE, DIMTYPE, SUBSET, DATAACCESSOR > > _model
the underlying multinomial model, learned/narrowed by train()
Definition classifier_multinom_naivebayes.hpp:174
virtual bool test_confusion(std::vector< RETURNTYPE > &confusion, DIMTYPE &classes, const PDataAccessor da) override
Definition classifier_multinom_naivebayes.hpp:149
DIMTYPE get_d() const
the underlying model's current virtual (narrowed) dimensionality (Model_Multinomial::get_d())
Definition classifier_multinom_naivebayes.hpp:165
DIMTYPE _classes
class count, sized by initialize() (dimensions _Pcd)
Definition classifier_multinom_naivebayes.hpp:176
void initialize(const PDataAccessor da)
Definition classifier_multinom_naivebayes.hpp:231
DATAACCESSOR::PPattern PPattern
one data sample as exposed by DATAACCESSOR (a raw pointer to its feature vector)
Definition classifier_multinom_naivebayes.hpp:118
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying train/test blocks
Definition classifier_multinom_naivebayes.hpp:116
DIMTYPE _features
raw feature count, sized by initialize() (dimensions _index)
Definition classifier_multinom_naivebayes.hpp:177
void disable_prelearn_mode()
reverts to the default (per-train() relearning) mode; see enable_prelearn_mode()
Definition classifier_multinom_naivebayes.hpp:128
bool _prelearn_mode
see enable_prelearn_mode()/disable_prelearn_mode()/get_prelearn_mode()
Definition classifier_multinom_naivebayes.hpp:181
bool get_prelearn_mode() const
true if enable_prelearn_mode() is currently in effect
Definition classifier_multinom_naivebayes.hpp:129
virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override
Definition classifier_multinom_naivebayes.hpp:282
virtual bool test(RETURNTYPE &result, const PDataAccessor da) override
Definition classifier_multinom_naivebayes.hpp:334
virtual bool train(const PDataAccessor da, const PSubset sub) override
learns from designated training part of data
Definition classifier_multinom_naivebayes.hpp:246
DIMTYPE _subfeatures
number of features in the currently trained subset (sub->get_d_raw() at the last train() call)
Definition classifier_multinom_naivebayes.hpp:183
DIMTYPE get_n() const
the underlying model's full ("raw") learned dimensionality (Model_Multinomial::get_n())
Definition classifier_multinom_naivebayes.hpp:164
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the object; base default writes nothing
Definition classifier_multinom_naivebayes.hpp:169
std::unique_ptr< DIMTYPE[]> _index
subset position -> raw feature index (first _subfeatures entries valid), filled by train()
Definition classifier_multinom_naivebayes.hpp:182
Clonable_Impl< Classifier_Multinomial_NaiveBayes, Classifier< RETURNTYPE, DIMTYPE, SUBSET, DATAACCESSOR > > parent_impl
implements clone(); sharing/stateless_clone throw in Classifier
Definition classifier_multinom_naivebayes.hpp:115
std::unique_ptr< REALTYPE[]> _Pcd
per-class score of the pattern last passed to classify(): the UNNORMALIZED log-posterior ln P(c) + su...
Definition classifier_multinom_naivebayes.hpp:178
std::shared_ptr< SUBSET > const PSubset
shared pointer to the (fixed, non-reassignable) feature subset being trained/evaluated on
Definition classifier_multinom_naivebayes.hpp:117
void enable_prelearn_mode(const PDataAccessor da)
Definition classifier_multinom_naivebayes.hpp:219
virtual bool classify_scores(std::vector< RETURNTYPE > &scores, const PPattern &pattern) override
Definition classifier_multinom_naivebayes.hpp:139
virtual bool test_confusion(std::vector< RETURNTYPE > &confusion, DIMTYPE &classes, const PDataAccessor da)
Definition classifier.hpp:181
CRTP mixin implementing the Clonable protocol for a concrete class.
Definition clonable.hpp:184
Multinomial ("bag of words") model: per-class term-count sums and the Laplace-smoothed term probabili...
Definition model_multinom.hpp:120
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
Defines interface for classifier implementations.
FST4 specific error passing class.
Global definitions.
Implements multinomial 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
@ TEST
the split's test part
Definition data_accessor.hpp:86
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.