Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
regressor_ridge.hpp
Go to the documentation of this file.
1#ifndef FSTREGRESSORRIDGE_H
2#define FSTREGRESSORRIDGE_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 <cassert>
51#include <iostream>
52#include "error.hpp"
53#include "global.hpp"
54#include "clonable.hpp"
55#include "regressor.hpp"
56#include "dense_train_tools.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
118template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
119class Regressor_Ridge : public Clonable_Impl<Regressor_Ridge<RETURNTYPE,DATATYPE,REALTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>,Regressor<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR> > {
120public:
123 typedef std::shared_ptr<DATAACCESSOR> PDataAccessor;
124 typedef std::shared_ptr<SUBSET> PSubset;
125 typedef typename DATAACCESSOR::PPattern PPattern;
126 Regressor_Ridge() : _lambda(0.01), _dep_var_idx((DIMTYPE)-1), _features(0), _subfeatures(0) {notify("Regressor_Ridge constructor.");}
127 virtual ~Regressor_Ridge() {notify("Regressor_Ridge destructor.");}
128
129 void set_lambda(const REALTYPE l) {assert(l>=0.0); _lambda=l;}
135 void set_dependent_index(const DIMTYPE depvaridx) {_dep_var_idx=depvaridx;}
136
137 void initialize(const PDataAccessor da);
138
141 virtual bool predict(RETURNTYPE &depvar, const PPattern &pattern) override;
145 virtual bool train(const PDataAccessor da, const PSubset sub) override;
146 virtual bool test(RETURNTYPE &result, const PDataAccessor da) override; // the measure Regressor_Weighted_Neighbours::test() also reports (see the class documentation)
147 virtual bool test_metric(RETURNTYPE &result, const PDataAccessor da, const Metric_Spec<RETURNTYPE> &spec) override; // r2/neg-rmse/neg-mae
148
149 virtual std::ostream& print(std::ostream& os) const override {os << "Regressor_Ridge(lambda=" << _lambda << ")"; return os;}
150protected:
151 REALTYPE _lambda;
152 DIMTYPE _dep_var_idx;
153
154 DIMTYPE _features;
155 DIMTYPE _subfeatures;
156 std::vector<DIMTYPE> _index;
157 std::vector<REALTYPE> _mean;
158 std::vector<REALTYPE> _scale;
159 std::vector<REALTYPE> _w;
160
161 // training scratch (copied by clones harmlessly)
162 std::vector<REALTYPE> _A;
163 std::vector<REALTYPE> _b;
164 std::vector<REALTYPE> _Z;
165 std::vector<REALTYPE> _yv;
166
171 bool dependent_from(const PPattern &pat, const DIMTYPE cls, REALTYPE &dep) const
172 {
173 if(_dep_var_idx<_features) dep=(REALTYPE)pat[_dep_var_idx];
174 else dep=(REALTYPE)cls; // sentinel: class label as dependent variable (the wrapper-regressor pipeline convention)
175 return true;
176 }
177 // Cholesky factor/solve of the normal equations live in dense_train_tools.hpp (shared with classifier_logreg)
178 bool collect_predictions(const PDataAccessor da, std::vector<std::pair<RETURNTYPE,RETURNTYPE>> &pairs);
179};
180
181template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
183{
184 notify("Regressor_Ridge::initialize().");
185 assert(da);
186 assert(da->getNoOfFeatures()>0);
187 _features=da->getNoOfFeatures();
188 _index.resize(_features);
189 _subfeatures=0; // no model yet
190}
191
192template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
194{
195 notify("Regressor_Ridge::train().");
196 assert(da);
197 assert(sub);
198 assert(_features==da->getNoOfFeatures());
199 assert(sub->get_n_raw()==da->getNoOfFeatures());
200
201 // buffer the feature subset index, excluding the dependent column if it is one
202 _subfeatures=0;
203 for(DIMTYPE fsel : sub->selected_features()) {
204 assert(_subfeatures<_features);
205 if(fsel==_dep_var_idx) continue;
206 _index[_subfeatures++]=fsel;
207 }
208 if(_subfeatures==0) return false; // subset held only the dependent column
209
210 const std::size_t d=_subfeatures, dp1=d+1;
211
212 // pass 1 + standardize via the shared helper (dense_train_tools.hpp); the appended
213 // bias coordinate and the dependent value ride along in the per-pattern callback
214 _yv.clear();
215 const std::size_t N=standardize_design<REALTYPE,IDXTYPE>(da,da->getNoOfClasses(),d,dp1,_index,_Z,_mean,_scale,
216 [this](const auto *pat, const DIMTYPE c){_Z.push_back(1.0); REALTYPE dep; dependent_from(pat,c,dep); _yv.push_back(dep);});
217 if(N==0) return false;
218
219 // normal equations: A = Z^T Z / N + diag(lambda,...,lambda,epsilon); b = Z^T y / N
220 // NOTE the division by N happens BEFORE lambda is added, so lambda is a per-sample penalty
221 // (equivalent to N*lambda in the unscaled textbook formulation) - see the class documentation
222 _A.assign(dp1*dp1,0.0);
223 _b.assign(dp1,0.0);
224 for(std::size_t n=0;n<N;n++)
225 {
226 const REALTYPE *z=&_Z[n*dp1];
227 const REALTYPE y=_yv[n];
228 for(std::size_t f1=0;f1<dp1;f1++)
229 {
230 _b[f1]+=z[f1]*y;
231 for(std::size_t f2=0;f2<=f1;f2++) _A[f1*dp1+f2]+=z[f1]*z[f2]; // lower triangle
232 }
233 }
234 for(std::size_t j=0;j<dp1*dp1;j++) _A[j]/=(REALTYPE)N;
235 for(std::size_t f=0;f<dp1;f++) _b[f]/=(REALTYPE)N;
236 for(std::size_t f=0;f<d;f++) _A[f*dp1+f]+=_lambda;
237 // bias unpenalized; the tiny ridge only keeps Cholesky defined on degenerate data. Its size
238 // is arbitrary within reason - small enough not to shrink the bias measurably, large enough
239 // to dominate rounding noise in the scaled Gram matrix.
240 _A[d*dp1+d]+=(REALTYPE)1e-10;
241
242 // in-place lower-Cholesky of _A (SPD by construction: Gram + ridge), then two triangular solves into _w
243 if(!cholesky_factor(_A,dp1)) return false;
244 _w.assign(dp1,0.0);
245 cholesky_solve(_A,dp1,_b.data(),false,_w.data());
246 return true;
247}
248
249template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
251{
252 assert(_subfeatures>0); // train() must have succeeded
253 const std::size_t d=_subfeatures;
254 assert(_w.size()==d+1);
255 REALTYPE v=_w[d]; // bias
256 for(std::size_t f=0;f<d;f++) v+=_w[f]*(((REALTYPE)pattern[_index[f]]-_mean[f])/_scale[f]);
257 depvar=(RETURNTYPE)v;
258 return true;
259}
260
261template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
263{
264 // mirrors Regressor_Weighted_Neighbours::test(), so the two regressors report the same measure:
265 // label mode = fraction of predictions within 0.5 of the class label ("simulated classification"),
266 // column mode = mean of negated absolute prediction errors (FST maximizes)
267 notify("Regressor_Ridge::test().");
268 std::vector<std::pair<RETURNTYPE,RETURNTYPE>> pairs;
269 if(!collect_predictions(da,pairs)) return false;
270 if(pairs.empty()) return false;
271 RETURNTYPE errsum=0;
272 const bool label_mode=!(_dep_var_idx<_features);
273 for(const auto &pr : pairs)
274 {
275 if(label_mode) {if(std::fabs((REALTYPE)pr.first-(REALTYPE)pr.second)<0.5) errsum+=1;}
276 else errsum+=-std::fabs((REALTYPE)pr.first-(REALTYPE)pr.second);
277 }
278 result=errsum/(RETURNTYPE)pairs.size();
279 return true;
280}
281
282template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
284{
285 assert(da);
286 assert(_subfeatures>0);
287 typename DATAACCESSOR::PPattern p1;
288 IDXTYPE s1,i1;
289 const DIMTYPE _feats=da->getNoOfFeatures();
290 RETURNTYPE predval;
291 pairs.clear();
292 for(DIMTYPE c_test=0;c_test<da->getNoOfClasses();c_test++)
293 {
294 da->setClass(c_test);
295 for(const auto &blk : da->blocks(TEST)) { p1=blk.data; s1=blk.size;
296 for(i1=0;i1<s1;i1++)
297 {
298 REALTYPE depval;
299 dependent_from(&p1[i1*_feats],c_test,depval);
300 if(!predict(predval,&p1[i1*_feats])) return false;
301 pairs.push_back(std::make_pair((RETURNTYPE)depval,predval));
302 }
303 da->setClass(c_test); // necessary for the case of da pointing to the same object as a training accessor
304 }
305 }
306 return true;
307}
308
309template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
311{
312 if(spec.is_default()) return test(result,da); // the default metric IS test()'s own measure
313 if(!spec.is_regression_metric()) return false;
314 std::vector<std::pair<RETURNTYPE,RETURNTYPE>> pairs;
315 if(!collect_predictions(da,pairs)) return false;
316 return metric_from_predictions<RETURNTYPE>(result,spec.metric,pairs);
317}
318
319} // namespace
320#endif // FSTREGRESSORRIDGE_H ///:~
CRTP mixin implementing the Clonable protocol for a concrete class.
Definition clonable.hpp:184
Closed-form ridge regression (L2-regularized least squares) for wrapper-based feature selection.
Definition regressor_ridge.hpp:119
virtual bool test(RETURNTYPE &result, const PDataAccessor da) override
Scores the designated test data with the regressor's own default measure, oriented higher-is-better (...
Definition regressor_ridge.hpp:262
std::vector< REALTYPE > _b
d+1 right-hand side
Definition regressor_ridge.hpp:163
Regressor< RETURNTYPE, DIMTYPE, SUBSET, DATAACCESSOR > parent
the immediate abstract base
Definition regressor_ridge.hpp:121
std::vector< REALTYPE > _mean
standardization shift per subset position
Definition regressor_ridge.hpp:157
DIMTYPE _dep_var_idx
dependent-variable data column, or an out-of-range sentinel ((DIMTYPE)-1 by default) meaning "use the...
Definition regressor_ridge.hpp:152
std::vector< DIMTYPE > _index
subset position -> raw feature index (first d entries valid)
Definition regressor_ridge.hpp:156
void set_dependent_index(const DIMTYPE depvaridx)
Chooses the dependent variable: a raw data column index, or any out-of-range value (the (DIMTYPE)-1 d...
Definition regressor_ridge.hpp:135
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the object; base default writes nothing
Definition regressor_ridge.hpp:149
Clonable_Impl< Regressor_Ridge, parent > parent_impl
implements clone() via the (implicit, member-wise) copy-constructor - all members are value-semantic
Definition regressor_ridge.hpp:122
std::vector< REALTYPE > _yv
N dependent values.
Definition regressor_ridge.hpp:165
void set_lambda(const REALTYPE l)
Definition regressor_ridge.hpp:129
DIMTYPE _subfeatures
d = features used by the current model (dependent column excluded)
Definition regressor_ridge.hpp:155
std::vector< REALTYPE > _scale
standardization scale per subset position (1 for constant features)
Definition regressor_ridge.hpp:158
std::vector< REALTYPE > _w
d weights + bias at [d]
Definition regressor_ridge.hpp:159
bool collect_predictions(const PDataAccessor da, std::vector< std::pair< RETURNTYPE, RETURNTYPE > > &pairs)
the shared test-part (dependent,prediction) enumeration behind test() and test_metric()
Definition regressor_ridge.hpp:283
std::vector< REALTYPE > _Z
N x (d+1) standardized design.
Definition regressor_ridge.hpp:164
DIMTYPE _features
raw feature count recorded by initialize(), for sizing _index and for dependent_from()'s column-vs-la...
Definition regressor_ridge.hpp:154
void initialize(const PDataAccessor da)
records data shape, allocates the index buffer
Definition regressor_ridge.hpp:182
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying train/test blocks
Definition regressor_ridge.hpp:123
virtual bool train(const PDataAccessor da, const PSubset sub) override
Definition regressor_ridge.hpp:193
std::vector< REALTYPE > _A
(d+1)x(d+1) normal-equation matrix, row-major
Definition regressor_ridge.hpp:162
DATAACCESSOR::PPattern PPattern
one data sample as exposed by DATAACCESSOR (a raw pointer to its feature vector)
Definition regressor_ridge.hpp:125
virtual bool test_metric(RETURNTYPE &result, const PDataAccessor da, const Metric_Spec< RETURNTYPE > &spec) override
Definition regressor_ridge.hpp:310
bool dependent_from(const PPattern &pat, const DIMTYPE cls, REALTYPE &dep) const
Definition regressor_ridge.hpp:171
virtual bool predict(RETURNTYPE &depvar, const PPattern &pattern) override
Definition regressor_ridge.hpp:250
REALTYPE _lambda
L2 penalty weight; set by set_lambda().
Definition regressor_ridge.hpp:151
std::shared_ptr< SUBSET > PSubset
shared pointer to the feature subset being trained/evaluated on
Definition regressor_ridge.hpp:124
abstract class, defines interface for Regressor implementations (mainly to be used in wrappers)
Definition regressor.hpp:102
Defines interface for classes that may need to get cloned.
Shared dense-training helpers: standardized-design build and Cholesky factor/solve.
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
std::size_t standardize_design(const std::shared_ptr< DATAACCESSOR > &da, const DIMTYPE classes, const std::size_t d, const std::size_t zstride, const std::vector< DIMTYPE > &index, std::vector< REALTYPE > &Z, std::vector< REALTYPE > &mean, std::vector< REALTYPE > &scale, AFTERPATTERN after_pattern)
Definition dense_train_tools.hpp:84
@ TEST
the split's test part
Definition data_accessor.hpp:86
bool cholesky_factor(std::vector< REALTYPE > &A, const std::size_t m)
Definition dense_train_tools.hpp:133
void cholesky_solve(const std::vector< REALTYPE > &A, const std::size_t m, const REALTYPE *rhs, const bool negate_rhs, REALTYPE *out)
Definition dense_train_tools.hpp:156
@ f1
classification, two-class data: F1 of the positive class, which is Metric_Spec::f1_positive (class in...
Definition wrapper_metrics.hpp:63
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.