1#ifndef FSTCLASSIFIERSVM_H
2#define FSTCLASSIFIERSVM_H
106template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
107class Classifier_LIBSVM :
public Clonable_Impl<Classifier_LIBSVM<RETURNTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>,Classifier<RETURNTYPE,DIMTYPE,SUBSET,DATAACCESSOR>,CLONE_TO_STATELESS,CLONE_INHERIT,CLONE_WEAK> {
116 typedef std::shared_ptr<SUBSET>
const PSubset;
119 virtual ~Classifier_LIBSVM() {
notify(
"Classifier_LIBSVM destructor.");
cleanup();}
196 bool optimize_parameters(
const PDataAccessor da,
const PSubset sub,
const int max_points=100,
const int max_throws=100,
const double lgC_min=-5,
const double lgC_max=9,
const double lggamma_min=-15,
const double lggamma_max=3,
const double lgcoef0_min=-2,
const double lgcoef0_max=5, std::ostream& os=std::cout);
199 virtual std::ostream&
print(std::ostream& os)
const override {os <<
"Classifier_LIBSVM(kernel="<<
get_kernel_type()<<
")";
return os;}
207 Classifier_LIBSVM(
const Classifier_LIBSVM& csvm,
int);
246 const DIMTYPE ftmp=
_index[f];
247 const double tmp=pattern[ftmp];
258template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
259Classifier_LIBSVM<RETURNTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::Classifier_LIBSVM(
const Classifier_LIBSVM& csvm,
int) :
266 notify(
"Classifier_LIBSVM (weak) copy-constructor.");
296template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
297Classifier_LIBSVM<RETURNTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::Classifier_LIBSVM()
299 notify(
"Classifier_LIBSVM constructor.");
306 parameters.weight_label=
nullptr;
307 parameters.weight=
nullptr;
312 parameters.svm_type=C_SVC;
313 parameters.kernel_type=RBF;
315 parameters.gamma=1.0;
316 parameters.coef0=1.0;
317 parameters.cache_size=10;
318 parameters.eps=0.001;
320 parameters.nr_weight=0;
321 parameters.weight_label=
nullptr;
322 parameters.weight=
nullptr;
325 parameters.shrinking=0;
326 parameters.probability=0;
327 svm_class_weighing=
false;
330template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
331bool Classifier_LIBSVM<RETURNTYPE,IDXTYPE,DIMTYPE,SUBSET,DATAACCESSOR>::optimize_parameters(
const PDataAccessor da,
const PSubset sub,
const int max_points,
const int max_throws,
const double lgC_min,
const double lgC_max,
const double lggamma_min,
const double lggamma_max,
const double lgcoef0_min,
const double lgcoef0_max, std::ostream& os)
333 typename PARAMSETLIST::iterator iter;
339 assert(sub->get_d()>0);
340 assert(max_points>0);
341 assert(max_throws>0);
342 assert(lgC_max>lgC_min);
343 assert(lggamma_max>lggamma_min);
344 assert(lgcoef0_max>lgcoef0_min);
345 const double lgCrange=lgC_max-lgC_min;
346 const double lggammarange=lggamma_max-lggamma_min;
347 const double lgcoef0range=lgcoef0_max-lgcoef0_min;
351 RETURNTYPE val, maxval, firstval;
352 RETURNTYPE result=0.0;
354 for(
bool b=da->getFirstSplit();b==
true;b=da->getNextSplit()) {
355 if(!
train(da,sub))
return false;
356 if(!
test(val,da))
return false;
360 firstval=maxval=val=result/(RETURNTYPE)cnt;
362 std::ostringstream sos;
363 sos <<
"SVM before optimization: accuracy=" << val <<
", C=" <<
get_parameter_C();
365 case LINEAR: sos << std::endl;
break;
373 for(
int i=0;i<max_points;i++)
375 double maxdistmin=0.0;
376 for(
int j=0;j<max_throws;j++)
384 for(iter=_paramlist.begin();iter!=_paramlist.end();iter++)
389 case LINEAR: dist=sqrt(((log(tmpset.
_Cpar)-log(iter->_Cpar))/lgCrange)*((log(tmpset.
_Cpar)-log(iter->_Cpar))/lgCrange));
break;
390 case RBF: dist=sqrt(((log(tmpset.
_Cpar)-log(iter->_Cpar))/lgCrange)*((log(tmpset.
_Cpar)-log(iter->_Cpar))/lgCrange)+((log(tmpset.
_gamma)-log(iter->_gamma))/lggammarange)*((log(tmpset.
_gamma)-log(iter->_gamma))/lggammarange));
break;
392 case SIGMOID: dist=sqrt(((log(tmpset.
_Cpar)-log(iter->_Cpar))/lgCrange)*((log(tmpset.
_Cpar)-log(iter->_Cpar))/lgCrange)+((log(tmpset.
_gamma)-log(iter->_gamma))/lggammarange)*((log(tmpset.
_gamma)-log(iter->_gamma))/lggammarange)+((log(tmpset.
_coef0)-log(iter->_coef0))/lgcoef0range)*((log(tmpset.
_coef0)-log(iter->_coef0))/lgcoef0range));
break;
399 assert(iter->_crit_value>0.0);
400 if(distmin<0 || dist*iter->_crit_value<distmin) distmin=dist*iter->_crit_value;
402 assert(distmin>=0.0);
403 if(distmin>maxdistmin)
416 for(
bool b=da->getFirstSplit();b==
true;b=da->getNextSplit()) {
417 if(!
train(da,sub))
return false;
418 if(!
test(val,da))
return false;
422 val=result/(RETURNTYPE)cnt;
424 _paramlist.push_back(bestset);
426 std::ostringstream sos;
428 if(val>maxval) {maxval=val; sos <<
"BEST accuracy=";}
else sos <<
"test accuracy=";
431 case LINEAR: sos << std::endl;
break;
440 assert(!_paramlist.empty());
441 iter=_paramlist.begin();
443 while(iter!=_paramlist.end())
445 if(iter->_crit_value>bestset.
_crit_value) bestset=(*iter);
452 std::ostringstream sos;
455 case LINEAR: sos << std::endl;
break;
460 sos <<
"Accuracy difference=" << bestset.
_crit_value-firstval << std::endl << std::endl;
466template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
477 problem.x = (
struct svm_node **)malloc((std::size_t)(
_all_patterns) *
sizeof(
struct svm_node *));
480 onepattern = (
struct svm_node *) malloc((std::size_t)(
_features+1) *
sizeof(
struct svm_node));
484 throw fst_error(
"Classifier_LIBSVM allocation problem (malloc failed).");
489 problem.x[p] = (
struct svm_node *) malloc((
long)(
_features+1) *
sizeof(
struct svm_node));
490 if(!
problem.x[p])
throw fst_error(
"Classifier_LIBSVM allocation problem (malloc failed).");
500 throw fst_error(
"Classifier_LIBSVM allocation problem.");
504template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
517 throw fst_error(
"Classifier_LIBSVM initialization problem.");
521template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
537 if(
model!=
nullptr) {svm_free_and_destroy_model(&
model);
model=
nullptr;}
547template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
551 assert(
_classes==da->getNoOfClasses());
552 assert(
_features==da->getNoOfFeatures());
568 std::ostringstream sos;
575 typename DATAACCESSOR::PPattern p,ptmp;
583 for(DIMTYPE c_train=0;c_train<
_classes;c_train++)
585 da->setClass(c_train);
586 for(
const auto &blk : da->blocks(
TRAIN))
for(i=0,p=blk.data,s=blk.size;i<s;i++)
596 problem.x[count][nzfeat].index=ftmp+1;
597 problem.x[count][nzfeat].value=tmp;
601 problem.x[count][nzfeat].index=-1;
607 if(
model!=
nullptr) svm_free_and_destroy_model(&
model);
613 throw fst_error(
"Classifier_LIBSVM::train() error.");
615 if(
model==
nullptr)
return false;
619template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
622 if(
model==
nullptr)
throw fst_error(
"Classifier_LIBSVM::classify(): no trained model. Note that clones do not inherit the trained state (LibSVM models can not be cloned) - call train() first.");
633 throw fst_error(
"Classifier_LIBSVM::classify() error.");
638template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
648 if(
model==
nullptr)
throw fst_error(
"Classifier_LIBSVM::classify_scores(): no trained model. Note that clones do not inherit the trained state (LibSVM models can not be cloned) - call train() first.");
651 const int k=svm_get_nr_class(
model);
655 if(k!=2)
return false;
658 scores.assign(2,(RETURNTYPE)0.0);
659 const int l0=
model->label[0], l1=
model->label[1];
660 if(l0<0 || l0>1 || l1<0 || l1>1)
return false;
661 scores[l0]=(RETURNTYPE)dec;
662 scores[l1]=(RETURNTYPE)(-dec);
666 if(k<2)
return false;
667 _dec.assign((
size_t)k*(
size_t)(k-1)/2,0.0);
669 scores.assign(
_classes,(RETURNTYPE)0.0);
671 for(
int i=0;i<k;i++)
for(
int j=i+1;j<k;j++,pos++) {
672 const int li=
model->label[i], lj=
model->label[j];
674 scores[li]+=(RETURNTYPE)
_dec[pos];
675 scores[lj]-=(RETURNTYPE)
_dec[pos];
681 throw fst_error(
"Classifier_LIBSVM::classify_scores() error.");
686template<
class RETURNTYPE,
typename IDXTYPE,
typename DIMTYPE,
class SUBSET,
class DATAACCESSOR>
690 assert(
_classes==da->getNoOfClasses());
691 assert(
_features==da->getNoOfFeatures());
693 if(
model==
nullptr)
throw fst_error(
"Classifier_LIBSVM::test(): no trained model. Note that clones do not inherit the trained state (LibSVM models can not be cloned) - call train() first.");
700 typename DATAACCESSOR::PPattern p;
702 IDXTYPE count, correct;
708 for(DIMTYPE c_test=0;c_test<
_classes;c_test++)
710 da->setClass(c_test);
711 for(
const auto &blk : da->blocks(
TEST))
for(i=0,p=blk.data,s=blk.size;i<s;i++)
714 if(clstmp==c_test) correct++;
719 result=(RETURNTYPE)correct/(RETURNTYPE)count;
722 std::ostringstream sos;
723 sos <<
" result=" << result << std::endl;
730 throw fst_error(
"Classifier_LIBSVM::test() error.");
Nested class to hold parameter candidates in the course of optimize_parameters() run.
Definition classifier_svm.hpp:221
double _Cpar
candidate C value (named to avoid clashing with the enclosing class's parameters.C)
Definition classifier_svm.hpp:228
void operator=(const ParameterSet &ps)
copy-assignment
Definition classifier_svm.hpp:226
double _coef0
candidate coef0 value
Definition classifier_svm.hpp:230
double _gamma
candidate gamma value
Definition classifier_svm.hpp:229
RETURNTYPE _crit_value
mean criterion value (accuracy, averaged over the accessor's split loop) this candidate achieved,...
Definition classifier_svm.hpp:227
ParameterSet(const RETURNTYPE crit_value, const double C, const double gamma, const double coef0)
constructs a fully-specified candidate
Definition classifier_svm.hpp:224
ParameterSet(const ParameterSet &ps)
copy-constructor
Definition classifier_svm.hpp:225
void initialize(const PDataAccessor da)
Must be called (once, or again after a dataset-shape change) before train()/test()....
Definition classifier_svm.hpp:505
std::shared_ptr< DATAACCESSOR > PDataAccessor
shared pointer to the data accessor supplying train/test blocks
Definition classifier_svm.hpp:115
std::shared_ptr< SUBSET > const PSubset
shared pointer to the (fixed) feature subset being trained/evaluated
Definition classifier_svm.hpp:116
std::unique_ptr< DIMTYPE[]> _index
subset position -> raw feature index map, filled by train() from sub->selected_features(); regrown on...
Definition classifier_svm.hpp:235
void set_parameter_gamma(double newgamma)
sets the LibSVM kernel gamma (RBF/POLY/SIGMOID)
Definition classifier_svm.hpp:137
DIMTYPE _subfeatures
current subset size (number of valid entries in _index[]), set by train()
Definition classifier_svm.hpp:236
Clonable_Impl< Classifier_LIBSVM, Classifier< RETURNTYPE, DIMTYPE, SUBSET, DATAACCESSOR >, CLONE_TO_STATELESS, CLONE_INHERIT, CLONE_WEAK > parent_impl
Definition classifier_svm.hpp:114
DIMTYPE _classes
class count, set by initialize()
Definition classifier_svm.hpp:211
void allocate()
Definition classifier_svm.hpp:467
double get_parameter_coef0() const
current LibSVM kernel coef0 (POLY/SIGMOID)
Definition classifier_svm.hpp:142
bool svm_class_weighing
vestigial class-weighing switch: set false by the constructor and never read – allocate() uncondition...
Definition classifier_svm.hpp:218
void fill_onepattern(const PPattern &pattern)
transfers the pattern's nonzero selected features into onepattern (the shared classify()/classify_sco...
Definition classifier_svm.hpp:239
bool optimize_parameters(const PDataAccessor da, const PSubset sub, const int max_points=100, const int max_throws=100, const double lgC_min=-5, const double lgC_max=9, const double lggamma_min=-15, const double lggamma_max=3, const double lgcoef0_min=-2, const double lgcoef0_max=5, std::ostream &os=std::cout)
Searches for better SVM hyperparameters C, gamma and coef0 and adopts the best set found.
Definition classifier_svm.hpp:331
virtual bool classify(DIMTYPE &cls, const PPattern &pattern) override
Definition classifier_svm.hpp:620
void set_parameter_coef0(double newcoef0)
sets the LibSVM kernel coef0 (POLY/SIGMOID)
Definition classifier_svm.hpp:138
IDXTYPE _all_patterns
total pattern count across all classes, set by initialize()
Definition classifier_svm.hpp:210
struct svm_node * onepattern
single-pattern LibSVM transfer buffer, filled by fill_onepattern() for classify()/classify_scores()
Definition classifier_svm.hpp:216
DATAACCESSOR::PPattern PPattern
one data sample as exposed by DATAACCESSOR
Definition classifier_svm.hpp:117
int get_kernel_type() const
see svm.h for admissible values
Definition classifier_svm.hpp:143
void set_kernel_type(int kernel_type)
LINEAR/POLY/RBF/SIGMOID – see svm.h for admissible values.
Definition classifier_svm.hpp:139
virtual bool classify_scores(std::vector< RETURNTYPE > &scores, const PPattern &pattern) override
Definition classifier_svm.hpp:639
void set_parameter_C(double newC)
sets the LibSVM regularization parameter C
Definition classifier_svm.hpp:136
double get_parameter_gamma() const
current LibSVM kernel gamma (RBF/POLY/SIGMOID)
Definition classifier_svm.hpp:141
struct svm_problem problem
LibSVM training data (problem.x/y), rebuilt by train() from the designated TRAIN blocks.
Definition classifier_svm.hpp:213
struct svm_model * model
the trained LibSVM model, or nullptr before the first successful train()
Definition classifier_svm.hpp:215
std::list< ParameterSet > PARAMSETLIST
the growing list of parameter candidates evaluated so far, in evaluation order
Definition classifier_svm.hpp:232
virtual bool test(RETURNTYPE &result, const PDataAccessor da) override
Definition classifier_svm.hpp:687
virtual bool train(const PDataAccessor da, const PSubset sub) override
Definition classifier_svm.hpp:548
std::vector< double > _dec
Definition classifier_svm.hpp:237
DIMTYPE _features
raw (full, unnarrowed) feature count, set by initialize()
Definition classifier_svm.hpp:212
struct svm_parameter parameters
LibSVM training parameters (C, gamma, kernel_type, ...) – see set_parameter_*()/set_kernel_type().
Definition classifier_svm.hpp:214
void cleanup()
Definition classifier_svm.hpp:522
virtual std::ostream & print(std::ostream &os) const override
writes a human-readable identification of the object; base default writes nothing
Definition classifier_svm.hpp:199
double get_parameter_C() const
current LibSVM regularization parameter C
Definition classifier_svm.hpp:140
CRTP mixin implementing the Clonable protocol for a concrete class.
Definition clonable.hpp:184
FST4 specific error passing class.
Definition error.hpp:61
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.
FST4-owned pseudo-random number generator (UCRT-compatible LCG), replacing platform-specific std::ran...
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
@ CLONE_TO_STATELESS
forward to stateless_clone() (Classifier_LIBSVM contract)
Definition clonable.hpp:152
@ CLONE_WEAK
new Derived(*this,0) via the weak/referencing copy-constructor, then set_cloned()
Definition clonable.hpp:150
@ CLONE_INHERIT
do not add behavior; defer to the Base class body (typically a family-level throwing default)
Definition clonable.hpp:147
int fst_rand()
Returns the next pseudo-random draw in 0..FST_RAND_MAX; use instead of std::rand().
Definition fst_rand.hpp:93
const int FST_RAND_MAX
Maximum value fst_rand() can return; use instead of the libc macro RAND_MAX.
Definition fst_rand.hpp:101
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