Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
regressor_weighted_neighbours.hpp
Go to the documentation of this file.
1#ifndef FSTREGRESSORWEIGHTEDNEIGHBOURS_H
2#define FSTREGRESSORWEIGHTEDNEIGHBOURS_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 <cmath>
53#ifndef M_PI
54#define M_PI 3.14159265358979323846
55#endif
56#include <list>
57#include "regressor.hpp"
58#include "error.hpp"
59#include "global.hpp"
60
61/*============== Template parameter type naming conventions ==============
62--------- Numeric types: -------------------------------------------------
63DATATYPE - data sample values - usually real numbers (but may be integers
64 in text processing etc.)
65REALTYPE - must be real numbers - for representing intermediate results of
66 calculations like mean, covariance etc.
67IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
68 extent depends on numbers of samples in data
69DIMTYPE - index values for enumeration of features (dimensions), or classes (not
70 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
71 but be aware of expressions like _classes*_features*_features !
72 in linearized representations of feature matrices for all classes
73BINTYPE - feature selection marker type - represents ca. <10 different feature
74 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
75RETURNTYPE - criterion value: real value, but may be extended in future to support
76 multiple values
77--------- Class types: ---------------------------------------------------
78SUBSET - class of class type Subset
79CLASSIFIER - class implementing interface defined in abstract class Classifier
80EVALUATOR - class implementing interface defined in abstract class Sequential_Step
81DISTANCE - class implementing interface defined in abstract class Distance
82DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
83INTERVALCONTAINER - class of class type Data_Intervaller
84CONTAINER - STL container of class type Data_Interval
85========================================================================== */
86
87namespace FST {
88
117template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
118class Regressor_Weighted_Neighbours : public Clonable_Impl<Regressor_Weighted_Neighbours<RETURNTYPE,DATATYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR,DISTANCE>,Regressor<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR> > {
119public:
122 typedef std::shared_ptr<DATAACCESSOR> PDataAccessor;
123 typedef std::shared_ptr<SUBSET> PSubset;
124 typedef typename DATAACCESSOR::PPattern PPattern;
125 Regressor_Weighted_Neighbours() : _dep_var_idx((IDXTYPE)-1), _dist_mean(0), _dist_stddev(0), _kernel_multiplier(1) {notify("Regressor_Weighted_Neighbours constructor.");}
126 virtual ~Regressor_Weighted_Neighbours() {notify("Regressor_Weighted_Neighbours destructor.");}
127
128 void set_kernel_multiplier(const RETURNTYPE m) {assert(m > 0); _kernel_multiplier = m;}
129 RETURNTYPE get_kernel_size() const {return _dist_stddev * _kernel_multiplier;}
130 void set_dependent_index(const IDXTYPE depvaridx) {_dep_var_idx = depvaridx;}
131
132 virtual bool predict(RETURNTYPE &depvar, const PPattern &pattern) override; // predicts dependent variable for pattern
139 virtual bool train(const PDataAccessor da, const PSubset sub) override;
143 virtual bool test(RETURNTYPE &result, const PDataAccessor da) override;
144 virtual bool test_metric(RETURNTYPE &result, const PDataAccessor da, const Metric_Spec<RETURNTYPE> &spec) override; // r2/neg-rmse/neg-mae over the same dependent-variable convention as test()
145
146
147
148 virtual std::ostream& print(std::ostream& os) const override;
149private:
150 friend parent_impl;
151 Regressor_Weighted_Neighbours(const Regressor_Weighted_Neighbours& cknn);
152protected:
153 virtual RETURNTYPE get_weight(RETURNTYPE dist);
154 DIMTYPE _dep_var_idx;
155 std::unique_ptr<DISTANCE> _distance;
156 RETURNTYPE _dist_mean;
157 RETURNTYPE _dist_stddev;
160};
161
162template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
163Regressor_Weighted_Neighbours<RETURNTYPE,DATATYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR,DISTANCE>::Regressor_Weighted_Neighbours(const Regressor_Weighted_Neighbours& rwn) :
168{
169 notify("Regressor_Weighted_Neighbours copy-constructor.");
171}
172
173
174
175
176template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
178{
179 assert(da);
180 assert(sub);
181 if(!_distance || _distance->get_n()<sub->get_n_raw()) _distance.reset(new DISTANCE(sub->get_n_raw()));
182 // No dependent COLUMN in range (the default sentinel, or an out-of-range index): the class
183 // label is the dependent variable and the distance uses the subset as it stands. Otherwise
184 // the distance must be measured WITHOUT the dependent column - a copy of the subset with
185 // that feature deselected is narrowed to instead, leaving the caller's subset untouched.
186 if(_dep_var_idx >= sub->get_n_raw()) {
187 _dep_var_idx = (IDXTYPE)-1;
188 _distance->narrow_to(sub);
189 } else {
190 PSubset dsub(new SUBSET(*sub)); // clone
191 dsub->deselect_raw(_dep_var_idx);
192 _distance->narrow_to(dsub);
193 }
194 _da_train=da;
195
196 // recompute kernel weight params
197 assert(_distance);
198 assert(_distance->get_n()==_da_train->getNoOfFeatures());
199 assert(_distance->get_d()>0);
200 typename DATAACCESSOR::PPattern p0,p1;
201 IDXTYPE s0,i0, s1,i1;
202 DIMTYPE _feats=_da_train->getNoOfFeatures();
203 RETURNTYPE dist;
204
205 _dist_mean = 0;
206 IDXTYPE _dist_count = 0;
207 typename std::list<RETURNTYPE> _dists;
208 typename std::list<RETURNTYPE>::const_iterator diter;
209
210
211 for(DIMTYPE c_train0=0;c_train0<_da_train->getNoOfClasses();c_train0++)
212 {
213 _da_train->setClass(c_train0);
214 for(const auto &blk0 : _da_train->blocks(TRAIN)) for(i0=0,p0=blk0.data,s0=blk0.size;i0<s0;i0++)
215 {
216
217 for(DIMTYPE c_train1=0;c_train1<_da_train->getNoOfClasses();c_train1++)
218 {
219 _da_train->setClass(c_train1);
220 for(const auto &blk1 : _da_train->blocks(TRAIN)) for(i1=0,p1=blk1.data,s1=blk1.size;i1<s1;i1++)
221 {
222 // p1[i1*_feats] is the beginning of the current pattern
223 dist=_distance->distance(&p0[i0*_feats],&p1[i1*_feats]);
224 // zero distances are skipped: they include every pattern
225 // against itself, and would otherwise drag the kernel
226 // width towards 0. Exact duplicates in the data are
227 // skipped as well, as a side effect.
228 if(dist > 0) {
229 _dists.push_back(dist);
230 _dist_mean += dist;
231 _dist_count++;
232 }
233 }
234 }
235 _da_train->setClass(c_train0);
236 }
237 }
238 // _dist_count==0 means every pair of training patterns coincides (or there is only one
239 // pattern): there is no scale to build a kernel from. With assertions disabled this
240 // divides by zero and leaves the kernel width NaN, which propagates into every
241 // prediction - the degenerate case named in the class documentation.
242 assert(_dist_count > 0);
243 assert(_dist_count == _dists.size());
244 _dist_mean /= (RETURNTYPE)_dist_count;
245 _dist_stddev = 0;
246 for(diter = _dists.begin(); diter != _dists.end(); diter++) {
247 _dist_stddev += (*diter - _dist_mean) * (*diter - _dist_mean);
248 }
249 _dist_stddev = sqrt(_dist_stddev / (RETURNTYPE)_dist_count); // population divisor, not _dist_count-1
250 return true;
251}
252
253template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
255{
256 // Gaussian kernel of width get_kernel_size(). The normalizing factor 1/(stddev*sqrt(2pi))
257 // is the same for every neighbour and thus cancels in predict()'s weighted average; it is
258 // kept so that the returned value is a proper density. A zero kernel width (identical
259 // training patterns, see train()) divides by zero here.
260 const RETURNTYPE stddev = _dist_stddev * _kernel_multiplier;
261 return exp( -(dist*dist)/(2.0*stddev*stddev) ) / (stddev * sqrt(2.0*M_PI));
262}
263
264template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
266{
267 // NOTE: iterates training data via its own RAII block range
268 // NOTE: da and _da_train may point to the same instance -> be careful with setClass() etc.
269 assert(_da_train);
270 assert(_distance);
271 assert(_distance->get_n()==_da_train->getNoOfFeatures());
272 assert(_distance->get_d()>0);
273 typename DATAACCESSOR::PPattern p2;
274 IDXTYPE s2,i2;
275 const DIMTYPE _feats=_da_train->getNoOfFeatures();
276 RETURNTYPE dist;
277 RETURNTYPE weight;
278
279 RETURNTYPE predval = 0;
280 RETURNTYPE weightsum = 0;
281
282
283
284
285 for(DIMTYPE c_train=0;c_train<_da_train->getNoOfClasses();c_train++)
286 {
287 _da_train->setClass(c_train);
288 for(const auto &blk : _da_train->blocks(TRAIN)) for(i2=0,p2=blk.data,s2=blk.size;i2<s2;i2++)
289 {
290 RETURNTYPE depval;
291 if(_dep_var_idx == (IDXTYPE)-1) {
292 depval = c_train; // the documented primary path: class label = dependent variable ("regression simulating classification", the -1 sentinel)
293 } else {
294 depval = p2[i2*_feats + _dep_var_idx];
295 }
296 // p2[i2*_feats] is the beginning of the current pattern
297 dist=_distance->distance(pattern,&p2[i2*_feats]);
298
299 weight = get_weight( dist );
300 predval += weight * depval;
301 weightsum += weight;
302 }
303 }
304 // weightsum==0 requires every training pattern to be so far away that its Gaussian weight
305 // underflows to 0 (or a NaN kernel width from a degenerate train()); with assertions
306 // disabled the prediction becomes NaN or infinite rather than failing
307 assert(weightsum != 0);
308 depvar = predval / weightsum;
309 return true;
310}
311
312template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
314{
315 // NOTE: da and _da_train may point to the same instance -> be careful with setClass() etc.
316 assert(da);
317 assert(_da_train);
318 assert(_distance);
319 assert(da->getNoOfFeatures()==_da_train->getNoOfFeatures());
320 assert(_distance->get_n()==_da_train->getNoOfFeatures());
321 typename DATAACCESSOR::PPattern p1;
322 IDXTYPE s1,i1;
323 IDXTYPE count=0;
324 RETURNTYPE errsum = 0; // despite the name: a HIT count in class-label mode, a negated error sum otherwise
325 DIMTYPE _feats=da->getNoOfFeatures();
326 RETURNTYPE predval;
327
328
329
330 for(DIMTYPE c_test=0;c_test<da->getNoOfClasses();c_test++)
331 {
332 da->setClass(c_test);
333 for(const auto &blk : da->blocks(TEST)) { p1=blk.data; s1=blk.size;
334 for(i1=0;i1<s1;i1++)
335 {
336 RETURNTYPE depval;
337 if(_dep_var_idx == (IDXTYPE)-1) {
338 depval = c_test; // the documented primary path: class label = dependent variable ("regression simulating classification", the -1 sentinel)
339 } else {
340 depval = p1[i1*_feats + _dep_var_idx];
341 }
342
343 // predict() runs its own block loop over the TRAIN part of
344 // _da_train, which may be this very accessor - hence the setClass()
345 // restore after each block below
346 if(!predict(predval, &p1[i1*_feats])) return false;
347
348 if(_dep_var_idx == (IDXTYPE)-1) {
349 if( std::abs( depval - predval ) < 0.5 ) errsum += 1; // simulate classification (std:: qualified: bare abs(double) risks the int overload)
350 } else {
351 errsum += - std::abs( depval - predval ); // sum negatives of prediction errors (FST maximizes; std:: qualified: bare abs(double) risks the int overload)
352 }
353 ++count;
354 }
355 da->setClass(c_test); // necessary for the case of da pointing to the same object as _da_train
356 }
357 }
358 if(count==0) return false;
359 result = (RETURNTYPE)errsum/(RETURNTYPE)count;
360 return true;
361}
362
363template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
365{
366 // the test() loop, collecting (true value, prediction) pairs for r2/neg-rmse/neg-mae;
367 // the dependent-variable convention (data column, or class label when _dep_var_idx==-1) is test()'s
368 // NOTE: da and _da_train may point to the same instance -> be careful with setClass() etc.
369 if(spec.is_default()) return test(result,da); // the default metric IS test()'s own measure
370 if(!spec.is_regression_metric()) return false; // classification metrics do not apply to regression stacks
371 assert(da);
372 assert(_da_train);
373 assert(_distance);
374 typename DATAACCESSOR::PPattern p1;
375 IDXTYPE s1,i1;
376 DIMTYPE _feats=da->getNoOfFeatures();
377 RETURNTYPE predval;
378 std::vector<std::pair<RETURNTYPE,RETURNTYPE>> pairs;
379
380 for(DIMTYPE c_test=0;c_test<da->getNoOfClasses();c_test++)
381 {
382 da->setClass(c_test);
383 for(const auto &blk : da->blocks(TEST)) { p1=blk.data; s1=blk.size;
384 for(i1=0;i1<s1;i1++)
385 {
386 RETURNTYPE depval;
387 if(_dep_var_idx == (IDXTYPE)-1) depval = c_test; // the documented primary path: class label = dependent variable ("regression simulating classification", the -1 sentinel)
388 else depval = p1[i1*_feats + _dep_var_idx];
389 if(!predict(predval, &p1[i1*_feats])) return false;
390 pairs.push_back(std::make_pair(depval,predval));
391 }
392 da->setClass(c_test); // necessary for the case of da pointing to the same object as _da_train
393 }
394 }
395 return metric_from_predictions<RETURNTYPE>(result,spec.metric,pairs);
396}
397
398template<class RETURNTYPE, typename DATATYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR, class DISTANCE>
400{
401 os << "Regressor_Weighted_Neighbours()";
402 if(_distance) os << *_distance;
403 return os;
404}
405
406} // namespace
407#endif // FSTREGRESSORWEIGHTEDNEIGHBOURS_H ///:~
CRTP mixin implementing the Clonable protocol for a concrete class.
Definition clonable.hpp:184
Implements experimental regressor that produces weighted average of the predicted variable over neigh...
Definition regressor_weighted_neighbours.hpp:118
virtual bool test(RETURNTYPE &result, const PDataAccessor da) override
Definition regressor_weighted_neighbours.hpp:313
Regressor< RETURNTYPE, DIMTYPE, SUBSET, DATAACCESSOR > parent
Definition regressor_weighted_neighbours.hpp:120
RETURNTYPE get_kernel_size() const
the effective kernel width (training-set pairwise-distance stddev times the multiplier set by set_ker...
Definition regressor_weighted_neighbours.hpp:129
void set_dependent_index(const IDXTYPE depvaridx)
data column to predict; the (IDXTYPE)-1 default/sentinel selects the class label instead ("regression...
Definition regressor_weighted_neighbours.hpp:130
void set_kernel_multiplier(const RETURNTYPE m)
scales the Gaussian kernel width used by get_weight(); width = distance stddev * m (must be > 0)
Definition regressor_weighted_neighbours.hpp:128
DATAACCESSOR::PPattern PPattern
Definition regressor_weighted_neighbours.hpp:124
Clonable_Impl< Regressor_Weighted_Neighbours, parent > parent_impl
Definition regressor_weighted_neighbours.hpp:121
std::shared_ptr< DATAACCESSOR > PDataAccessor
Definition regressor_weighted_neighbours.hpp:122
std::unique_ptr< DIST_L2 > _distance
Definition regressor_weighted_neighbours.hpp:155
virtual bool train(const PDataAccessor da, const PSubset sub) override
Definition regressor_weighted_neighbours.hpp:177
virtual bool predict(RETURNTYPE &depvar, const PPattern &pattern) override
Predicts the dependent variable of one pattern into depvar (see the dependent-variable convention in ...
Definition regressor_weighted_neighbours.hpp:265
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the object; base default writes nothing
Definition regressor_weighted_neighbours.hpp:399
virtual bool test_metric(RETURNTYPE &result, const PDataAccessor da, const Metric_Spec< RETURNTYPE > &spec) override
Definition regressor_weighted_neighbours.hpp:364
virtual RETURNTYPE get_weight(RETURNTYPE dist)
the (unnormalized) Gaussian kernel weight for a neighbour at distance dist, using get_kernel_size() a...
Definition regressor_weighted_neighbours.hpp:254
Regressor_Weighted_Neighbours(const Regressor_Weighted_Neighbours &cknn)
copy-constructor (clones _distance if allocated; scalar members are value-copied)
Definition regressor_weighted_neighbours.hpp:163
std::shared_ptr< SUBSET > PSubset
Definition regressor_weighted_neighbours.hpp:123
abstract class, defines interface for Regressor implementations (mainly to be used in wrappers)
Definition regressor.hpp:102
#define M_PI
value of pi; POSIX <cmath> extension, provided here as a fallback since strict C++17 does not guarant...
Definition classifier_normal_bayes.hpp:55
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
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
@ TRAIN
the split's training part
Definition data_accessor.hpp:85
std::unique_ptr< T > clone_unique(const T &src)
Definition clonable.hpp:234
bool metric_from_predictions(RETURNTYPE &result, const Wrapper_Metric metric, const std::vector< std::pair< RETURNTYPE, RETURNTYPE > > &pairs)
Definition wrapper_metrics.hpp:245
Defines interface for regressor implementations.
configuration of the wrapper metric; immutable during search, copied by criterion clones (thread-safe...
Definition wrapper_metrics.hpp:73
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
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.