Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
classifier_linear_svm.hpp
Go to the documentation of this file.
1#ifndef FSTCLASSIFIERLINSVM_H
2#define FSTCLASSIFIERLINSVM_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 <limits>
51#include <cassert>
52#include <iostream>
53#include "error.hpp"
54#include "global.hpp"
55#include "dense_train_tools.hpp"
56#include "clonable.hpp"
57#include "classifier.hpp"
58
59/*============== Template parameter type naming conventions ==============
60--------- Numeric types: -------------------------------------------------
61DATATYPE - data sample values - usually real numbers (but may be integers
62 in text processing etc.)
63REALTYPE - must be real numbers - for representing intermediate results of
64 calculations like mean, covariance etc.
65IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
66 extent depends on numbers of samples in data
67DIMTYPE - index values for enumeration of features (dimensions), or classes (not
68 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
69 but be aware of expressions like _classes*_features*_features !
70 in linearized representations of feature matrices for all classes
71BINTYPE - feature selection marker type - represents ca. <10 different feature
72 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
73RETURNTYPE - criterion value: real value, but may be extended in future to support
74 multiple values
75--------- Class types: ---------------------------------------------------
76SUBSET - class of class type Subset
77CLASSIFIER - class implementing interface defined in abstract class Classifier
78EVALUATOR - class implementing interface defined in abstract class Sequential_Step
79DISTANCE - class implementing interface defined in abstract class Distance
80DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
81INTERVALCONTAINER - class of class type Data_Intervaller
82CONTAINER - STL container of class type Data_Interval
83========================================================================== */
84
85namespace FST {
86
110template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
111class Classifier_Linear_SVM : public Clonable_Impl<Classifier_Linear_SVM<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_Linear_SVM() : _C(1.0), _l2loss(true), _max_iter(1000), _tol(0.1),
119 _classes(0), _features(0), _subfeatures(0) {notify("Classifier_Linear_SVM constructor.");}
120 virtual ~Classifier_Linear_SVM() {notify("Classifier_Linear_SVM destructor.");}
121
122 void set_parameter_C(const REALTYPE c) {assert(c>0.0); _C=c;}
123 void set_l2_loss(const bool l2) {_l2loss=l2;}
124 void set_max_iter(const unsigned int mi) {assert(mi>0); _max_iter=mi;}
125 void set_tolerance(const REALTYPE t) {assert(t>0.0); _tol=t;}
126
129 void initialize(const PDataAccessor da);
130
131 virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override; // argmax of per-class decision values; first maximum wins
132 virtual bool classify_scores(std::vector<RETURNTYPE> &scores, const PPattern &pattern) override; // per-class decision values for ranking metrics (AUC)
139 virtual bool train(const PDataAccessor da, const PSubset sub) override;
140 // test() = the canonical accuracy loop inherited from Classifier, driven by classify() above
141
142 virtual std::ostream& print(std::ostream& os) const override
143 {os << "Classifier_Linear_SVM(C=" << _C << ", loss=" << (_l2loss?"l2":"l1") << ", max_iter=" << _max_iter << ", tol=" << _tol << ")"; return os;}
144protected:
145 // configuration
146 REALTYPE _C;
147 bool _l2loss;
148 unsigned int _max_iter;
149 REALTYPE _tol;
150
151 // data shape + trained state
152 DIMTYPE _classes;
153 DIMTYPE _features;
154 DIMTYPE _subfeatures;
155 std::vector<DIMTYPE> _index;
156 std::vector<RETURNTYPE> _cs_sc;
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> _Z;
163 std::vector<DIMTYPE> _y;
164 std::vector<REALTYPE> _alpha;
165 std::vector<REALTYPE> _qbar;
166
167 void solve_dual(REALTYPE *w, const DIMTYPE positive_class);
171 DIMTYPE model_count() const {return (_classes==2)?1:_classes;}
172};
173
174template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
176{
177 notify("Classifier_Linear_SVM::initialize().");
178 assert(da);
179 assert(da->getNoOfFeatures()>0);
180 if(da->getNoOfClasses()<2) throw fst_error("Classifier_Linear_SVM requires at least 2 classes");
181 _classes=da->getNoOfClasses();
182 _features=da->getNoOfFeatures();
183 _index.resize(_features);
184 _subfeatures=0; // no model yet
185}
186
187template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
189{
190 // dual coordinate descent (Hsieh et al. 2008): min 1/2 a^T Qbar a - e^T a, 0<=a_i<=U,
191 // Qbar_ij = y_i y_j z_i^T z_j (+ diag 1/(2C) for L2-loss); w = sum a_i y_i z_i maintained.
192 // The two losses differ exactly in the box and the diagonal shift: L1-loss (hinge) has
193 // the finite box U=C and no shift, L2-loss (squared hinge) has U=infinity (the dual
194 // variables are unbounded) and the shift D_ii=1/(2C) in its place.
195 const std::size_t d=_subfeatures, dp1=d+1, N=_y.size();
196 const REALTYPE U=_l2loss?std::numeric_limits<REALTYPE>::infinity():_C;
197 const REALTYPE Dii=_l2loss?(REALTYPE)1.0/((REALTYPE)2.0*_C):(REALTYPE)0.0;
198
199 _alpha.assign(N,0.0);
200 for(std::size_t f=0;f<dp1;f++) w[f]=0.0;
201 for(unsigned int sweep=0;sweep<_max_iter;sweep++)
202 {
203 REALTYPE pg_max=0.0;
204 for(std::size_t i=0;i<N;i++) // FIXED cyclic order — deterministic by design (no RNG)
205 {
206 const REALTYPE *z=&_Z[i*dp1];
207 const REALTYPE yi=(_y[i]==positive_class)?(REALTYPE)1.0:(REALTYPE)-1.0;
208 REALTYPE g=0.0;
209 for(std::size_t f=0;f<dp1;f++) g+=w[f]*z[f];
210 g=yi*g-(REALTYPE)1.0+Dii*_alpha[i];
211
212 REALTYPE pg=g; // projected gradient at the box bounds
213 if(_alpha[i]<=0.0 && g>0.0) pg=0.0;
214 else if(_alpha[i]>=U && g<0.0) pg=0.0;
215 const REALTYPE apg=std::fabs(pg);
216 if(apg>pg_max) pg_max=apg;
217 if(apg>0.0)
218 {
219 const REALTYPE aold=_alpha[i];
220 REALTYPE anew=aold-g/_qbar[i];
221 if(anew<0.0) anew=0.0; else if(anew>U) anew=U;
222 _alpha[i]=anew;
223 const REALTYPE step=(anew-aold)*yi;
224 if(step!=0.0) for(std::size_t f=0;f<dp1;f++) w[f]+=step*z[f];
225 }
226 }
227 if(pg_max<_tol) break;
228 }
229}
230
231template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
233{
234 notify("Classifier_Linear_SVM::train().");
235 assert(da);
236 assert(sub);
237 assert(_classes==da->getNoOfClasses());
238 assert(_features==da->getNoOfFeatures());
239 assert(sub->get_n_raw()==da->getNoOfFeatures());
240
241 // buffer the feature subset index
242 _subfeatures=0;
243 for(DIMTYPE fsel : sub->selected_features()) {assert(_subfeatures<_features); _index[_subfeatures++]=fsel;}
244 assert(_subfeatures==sub->get_d_raw());
245 assert(_subfeatures>0);
246 const std::size_t d=_subfeatures, dp1=d+1;
247
248 // pass 1 + standardize via the shared helper (dense_train_tools.hpp); the appended
249 // bias coordinate and the label ride along in the per-pattern callback
250 _y.clear();
252 [this](const auto *pat, const DIMTYPE c){(void)pat; _Z.push_back(1.0); _y.push_back(c);});
253 if(N==0) return false;
254
255 // per-sample Qbar diagonal (z_i^T z_i + D_ii)
256 const REALTYPE Dii=_l2loss?(REALTYPE)1.0/((REALTYPE)2.0*_C):(REALTYPE)0.0;
257 _qbar.resize(N);
258 for(std::size_t n=0;n<N;n++)
259 {
260 REALTYPE q=0.0;
261 const REALTYPE *z=&_Z[n*dp1];
262 for(std::size_t f=0;f<dp1;f++) q+=z[f]*z[f];
263 _qbar[n]=q+Dii;
264 }
265
266 // one-vs-rest models (two-class data: the single model's +1 side is class index 0)
267 const DIMTYPE M=model_count();
268 _W.assign((std::size_t)M*dp1,0.0);
269 for(DIMTYPE m=0;m<M;m++) solve_dual(&_W[(std::size_t)m*dp1],m);
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 std::size_t d=_subfeatures, dp1=d+1;
278 scores.resize(_classes);
279 const DIMTYPE M=model_count();
280 for(DIMTYPE m=0;m<M;m++)
281 {
282 const REALTYPE *w=&_W[(std::size_t)m*dp1];
283 REALTYPE dec=w[d]; // bias
284 for(std::size_t f=0;f<d;f++) dec+=w[f]*(((REALTYPE)pattern[_index[f]]-_mean[f])/_scale[f]);
285 scores[m]=(RETURNTYPE)dec;
286 }
287 if(_classes==2) scores[1]=-scores[0]; // the single binary model scores both sides
288 return true;
289}
290
291template<class RETURNTYPE, typename DATATYPE, typename REALTYPE, typename IDXTYPE, typename DIMTYPE, class SUBSET, class DATAACCESSOR>
293{
294 if(!classify_scores(_cs_sc,pattern)) return false; // member scratch: avoids a per-pattern allocation
295 cls=0;
296 for(DIMTYPE c=1;c<_classes;c++) if(_cs_sc[c]>_cs_sc[cls]) cls=c; // first maximum wins
297 return true;
298}
299
300
301} // namespace
302#endif // FSTCLASSIFIERLINSVM_H ///:~
Dependency-free linear SVM trained by dual coordinate descent (the method of Hsieh et al....
Definition classifier_linear_svm.hpp:111
std::vector< REALTYPE > _W
models x (d+1) row-major; column d = bias; two-class data holds ONE model
Definition classifier_linear_svm.hpp:159
std::vector< DIMTYPE > _y
N training labels.
Definition classifier_linear_svm.hpp:163
void set_parameter_C(const REALTYPE c)
sets the SVM regularization parameter C (box constraint upper bound U under L1-loss)
Definition classifier_linear_svm.hpp:122
std::vector< DIMTYPE > _index
subset position -> raw feature index (first d entries valid)
Definition classifier_linear_svm.hpp:155
std::vector< REALTYPE > _qbar
N diagonal entries x_i^T x_i (+ 1/(2C) for L2-loss).
Definition classifier_linear_svm.hpp:165
Classifier< RETURNTYPE, DIMTYPE, SUBSET, DATAACCESSOR > parent
the immediate abstract base
Definition classifier_linear_svm.hpp:113
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the object; base default writes nothing
Definition classifier_linear_svm.hpp:142
virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override
Definition classifier_linear_svm.hpp:292
DIMTYPE _classes
class count, set by initialize()
Definition classifier_linear_svm.hpp:152
std::vector< REALTYPE > _mean
standardization shift per subset position
Definition classifier_linear_svm.hpp:157
virtual bool train(const PDataAccessor da, const PSubset sub) override
Trains the linear SVM: buffers the subset index, standardizes the design with an appended bias coordi...
Definition classifier_linear_svm.hpp:232
unsigned int _max_iter
cap on full coordinate sweeps in solve_dual()
Definition classifier_linear_svm.hpp:148
DIMTYPE model_count() const
Definition classifier_linear_svm.hpp:171
Clonable_Impl< Classifier_Linear_SVM, parent > parent_impl
implements clone() via the (implicit, member-wise) copy-constructor – all members are value-semantic
Definition classifier_linear_svm.hpp:114
std::vector< REALTYPE > _scale
standardization scale per subset position (1 for constant features)
Definition classifier_linear_svm.hpp:158
std::vector< RETURNTYPE > _cs_sc
classify() score scratch (member to avoid per-pattern allocation)
Definition classifier_linear_svm.hpp:156
void solve_dual(REALTYPE *w, const DIMTYPE positive_class)
Definition classifier_linear_svm.hpp:188
bool _l2loss
true = squared hinge (default), false = hinge
Definition classifier_linear_svm.hpp:147
DATAACCESSOR::PPattern PPattern
one data sample as exposed by DATAACCESSOR
Definition classifier_linear_svm.hpp:117
DIMTYPE _features
raw (full, unnarrowed) feature count, set by initialize()
Definition classifier_linear_svm.hpp:153
void initialize(const PDataAccessor da)
Records the class/feature counts of da and allocates the subset index buffer. Must be called before t...
Definition classifier_linear_svm.hpp:175
DIMTYPE _subfeatures
d = current subset size
Definition classifier_linear_svm.hpp:154
void set_tolerance(const REALTYPE t)
max |projected gradient| sweep-stopping threshold
Definition classifier_linear_svm.hpp:125
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying train/test blocks
Definition classifier_linear_svm.hpp:115
std::vector< REALTYPE > _Z
N x (d+1) standardized design incl. bias coordinate.
Definition classifier_linear_svm.hpp:162
std::vector< REALTYPE > _alpha
N dual variables of the model currently being trained (solve_dual() scratch).
Definition classifier_linear_svm.hpp:164
REALTYPE _C
regularization parameter, see set_parameter_C()
Definition classifier_linear_svm.hpp:146
void set_max_iter(const unsigned int mi)
cap on full coordinate sweeps
Definition classifier_linear_svm.hpp:124
REALTYPE _tol
max |projected gradient| sweep-stopping threshold
Definition classifier_linear_svm.hpp:149
std::shared_ptr< SUBSET > PSubset
shared pointer to the feature subset being trained/evaluated
Definition classifier_linear_svm.hpp:116
virtual bool classify_scores(std::vector< RETURNTYPE > &scores, const PPattern &pattern) override
Definition classifier_linear_svm.hpp:274
void set_l2_loss(const bool l2)
true = squared hinge (default), false = hinge
Definition classifier_linear_svm.hpp:123
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
FST4 specific error passing class.
Definition error.hpp:61
Defines interface for classifier implementations.
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
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.